diff --git a/Makefile b/Makefile deleted file mode 100644 index c9d46e7..0000000 --- a/Makefile +++ /dev/null @@ -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 diff --git a/meta/audio_3ds.wav b/meta/audio_3ds.wav deleted file mode 100644 index d977b62..0000000 Binary files a/meta/audio_3ds.wav and /dev/null differ diff --git a/meta/banner_3ds.cgfx b/meta/banner_3ds.cgfx deleted file mode 100644 index 7ea55df..0000000 Binary files a/meta/banner_3ds.cgfx and /dev/null differ diff --git a/meta/icon_3ds.png b/meta/icon_3ds.png deleted file mode 100644 index 40fec61..0000000 Binary files a/meta/icon_3ds.png and /dev/null differ diff --git a/meta/logo_3ds.bcma.lz b/meta/logo_3ds.bcma.lz deleted file mode 100644 index 1da003f..0000000 Binary files a/meta/logo_3ds.bcma.lz and /dev/null differ diff --git a/romfs/battery0.png b/romfs/battery0.png deleted file mode 100644 index e81551f..0000000 Binary files a/romfs/battery0.png and /dev/null differ diff --git a/romfs/battery1.png b/romfs/battery1.png deleted file mode 100644 index 41c11c9..0000000 Binary files a/romfs/battery1.png and /dev/null differ diff --git a/romfs/battery2.png b/romfs/battery2.png deleted file mode 100644 index 5a90b3d..0000000 Binary files a/romfs/battery2.png and /dev/null differ diff --git a/romfs/battery3.png b/romfs/battery3.png deleted file mode 100644 index 1b72515..0000000 Binary files a/romfs/battery3.png and /dev/null differ diff --git a/romfs/battery4.png b/romfs/battery4.png deleted file mode 100644 index 610647e..0000000 Binary files a/romfs/battery4.png and /dev/null differ diff --git a/romfs/battery5.png b/romfs/battery5.png deleted file mode 100644 index 1849318..0000000 Binary files a/romfs/battery5.png and /dev/null differ diff --git a/romfs/battery_charging.png b/romfs/battery_charging.png deleted file mode 100644 index 67633b8..0000000 Binary files a/romfs/battery_charging.png and /dev/null differ diff --git a/romfs/bottom_screen_bg.png b/romfs/bottom_screen_bg.png deleted file mode 100644 index c6ea9a7..0000000 Binary files a/romfs/bottom_screen_bg.png and /dev/null differ diff --git a/romfs/bottom_screen_bottom_bar.png b/romfs/bottom_screen_bottom_bar.png deleted file mode 100644 index 89124fd..0000000 Binary files a/romfs/bottom_screen_bottom_bar.png and /dev/null differ diff --git a/romfs/bottom_screen_bottom_bar_shadow.png b/romfs/bottom_screen_bottom_bar_shadow.png deleted file mode 100644 index e0ff43f..0000000 Binary files a/romfs/bottom_screen_bottom_bar_shadow.png and /dev/null differ diff --git a/romfs/bottom_screen_top_bar.png b/romfs/bottom_screen_top_bar.png deleted file mode 100644 index 89124fd..0000000 Binary files a/romfs/bottom_screen_top_bar.png and /dev/null differ diff --git a/romfs/bottom_screen_top_bar_shadow.png b/romfs/bottom_screen_top_bar_shadow.png deleted file mode 100644 index 31cbd9c..0000000 Binary files a/romfs/bottom_screen_top_bar_shadow.png and /dev/null differ diff --git a/romfs/button.png b/romfs/button.png deleted file mode 100644 index bb92c00..0000000 Binary files a/romfs/button.png and /dev/null differ diff --git a/romfs/logo.png b/romfs/logo.png deleted file mode 100644 index 857442b..0000000 Binary files a/romfs/logo.png and /dev/null differ diff --git a/romfs/meta_info_box.png b/romfs/meta_info_box.png deleted file mode 100644 index 5968f67..0000000 Binary files a/romfs/meta_info_box.png and /dev/null differ diff --git a/romfs/meta_info_box_shadow.png b/romfs/meta_info_box_shadow.png deleted file mode 100644 index 3e526af..0000000 Binary files a/romfs/meta_info_box_shadow.png and /dev/null differ diff --git a/romfs/progress_bar_bg.png b/romfs/progress_bar_bg.png deleted file mode 100644 index 7a7178d..0000000 Binary files a/romfs/progress_bar_bg.png and /dev/null differ diff --git a/romfs/progress_bar_content.png b/romfs/progress_bar_content.png deleted file mode 100644 index 79d2151..0000000 Binary files a/romfs/progress_bar_content.png and /dev/null differ diff --git a/romfs/scroll_bar.png b/romfs/scroll_bar.png deleted file mode 100644 index f4a985e..0000000 Binary files a/romfs/scroll_bar.png and /dev/null differ diff --git a/romfs/selection_overlay.png b/romfs/selection_overlay.png deleted file mode 100644 index c3ad8b3..0000000 Binary files a/romfs/selection_overlay.png and /dev/null differ diff --git a/romfs/textcolor.cfg b/romfs/textcolor.cfg deleted file mode 100644 index 30d175f..0000000 --- a/romfs/textcolor.cfg +++ /dev/null @@ -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 diff --git a/romfs/top_screen_bg.png b/romfs/top_screen_bg.png deleted file mode 100644 index 7e63b2a..0000000 Binary files a/romfs/top_screen_bg.png and /dev/null differ diff --git a/romfs/top_screen_bottom_bar.png b/romfs/top_screen_bottom_bar.png deleted file mode 100644 index 73178b0..0000000 Binary files a/romfs/top_screen_bottom_bar.png and /dev/null differ diff --git a/romfs/top_screen_bottom_bar_shadow.png b/romfs/top_screen_bottom_bar_shadow.png deleted file mode 100644 index da14ec9..0000000 Binary files a/romfs/top_screen_bottom_bar_shadow.png and /dev/null differ diff --git a/romfs/top_screen_top_bar.png b/romfs/top_screen_top_bar.png deleted file mode 100644 index 73178b0..0000000 Binary files a/romfs/top_screen_top_bar.png and /dev/null differ diff --git a/romfs/top_screen_top_bar_shadow.png b/romfs/top_screen_top_bar_shadow.png deleted file mode 100644 index d9ebee0..0000000 Binary files a/romfs/top_screen_top_bar_shadow.png and /dev/null differ diff --git a/romfs/wifi0.png b/romfs/wifi0.png deleted file mode 100644 index e5bb084..0000000 Binary files a/romfs/wifi0.png and /dev/null differ diff --git a/romfs/wifi1.png b/romfs/wifi1.png deleted file mode 100644 index ad4e031..0000000 Binary files a/romfs/wifi1.png and /dev/null differ diff --git a/romfs/wifi2.png b/romfs/wifi2.png deleted file mode 100644 index 4447d2e..0000000 Binary files a/romfs/wifi2.png and /dev/null differ diff --git a/romfs/wifi3.png b/romfs/wifi3.png deleted file mode 100644 index 9e89272..0000000 Binary files a/romfs/wifi3.png and /dev/null differ diff --git a/romfs/wifi_disconnected.png b/romfs/wifi_disconnected.png deleted file mode 100644 index 8c4d0b2..0000000 Binary files a/romfs/wifi_disconnected.png and /dev/null differ diff --git a/romfs/zh_cn.bcfnt b/romfs/zh_cn.bcfnt deleted file mode 100755 index 7a5804c..0000000 Binary files a/romfs/zh_cn.bcfnt and /dev/null differ diff --git a/servefiles/README.md b/servefiles/README.md deleted file mode 100644 index 99903da..0000000 --- a/servefiles/README.md +++ /dev/null @@ -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 diff --git a/servefiles/drop cias or folder here.bat b/servefiles/drop cias or folder here.bat deleted file mode 100644 index 19f1cc0..0000000 --- a/servefiles/drop cias or folder here.bat +++ /dev/null @@ -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 diff --git a/servefiles/drop cias or folder here.sh b/servefiles/drop cias or folder here.sh deleted file mode 100644 index 8414f07..0000000 --- a/servefiles/drop cias or folder here.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -read -p "Type the IP address of your 3DS: " -e input -python servefiles.py $input . diff --git a/servefiles/sendurls.py b/servefiles/sendurls.py deleted file mode 100644 index 2dbbd2b..0000000 --- a/servefiles/sendurls.py +++ /dev/null @@ -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] + ' ...') - 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) diff --git a/servefiles/servefiles.py b/servefiles/servefiles.py deleted file mode 100644 index 9fb4a4d..0000000 --- a/servefiles/servefiles.py +++ /dev/null @@ -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] + ' [host ip] [host port]') - sys.exit(1) - - interactive = True - -elif len(sys.argv) < 3 or len(sys.argv) > 6: - print('Usage: ' + sys.argv[0] + ' [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() diff --git a/servefiles/setup.py b/servefiles/setup.py deleted file mode 100644 index b65684e..0000000 --- a/servefiles/setup.py +++ /dev/null @@ -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" -) diff --git a/source/core/clipboard.c b/source/core/clipboard.c deleted file mode 100644 index ae12cd8..0000000 --- a/source/core/clipboard.c +++ /dev/null @@ -1,57 +0,0 @@ -#include -#include - -#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); -} \ No newline at end of file diff --git a/source/core/clipboard.h b/source/core/clipboard.h deleted file mode 100644 index c33265a..0000000 --- a/source/core/clipboard.h +++ /dev/null @@ -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(); \ No newline at end of file diff --git a/source/core/core.h b/source/core/core.h deleted file mode 100644 index 043c13b..0000000 --- a/source/core/core.h +++ /dev/null @@ -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" \ No newline at end of file diff --git a/source/core/data/bnr.c b/source/core/data/bnr.c deleted file mode 100644 index b08ad60..0000000 --- a/source/core/data/bnr.c +++ /dev/null @@ -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]; -} \ No newline at end of file diff --git a/source/core/data/bnr.h b/source/core/data/bnr.h deleted file mode 100644 index 2425a02..0000000 --- a/source/core/data/bnr.h +++ /dev/null @@ -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); \ No newline at end of file diff --git a/source/core/data/cia.c b/source/core/data/cia.c deleted file mode 100644 index 0e4ede0..0000000 --- a/source/core/data/cia.c +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/source/core/data/cia.h b/source/core/data/cia.h deleted file mode 100644 index f1831e4..0000000 --- a/source/core/data/cia.h +++ /dev/null @@ -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); \ No newline at end of file diff --git a/source/core/data/data.h b/source/core/data/data.h deleted file mode 100644 index 2561d3b..0000000 --- a/source/core/data/data.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include "bnr.h" -#include "cia.h" -#include "smdh.h" -#include "ticket.h" -#include "tmd.h" \ No newline at end of file diff --git a/source/core/data/smdh.c b/source/core/data/smdh.c deleted file mode 100644 index f1f5d29..0000000 --- a/source/core/data/smdh.c +++ /dev/null @@ -1,73 +0,0 @@ -#include - -#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]; -} diff --git a/source/core/data/smdh.h b/source/core/data/smdh.h deleted file mode 100644 index cb13651..0000000 --- a/source/core/data/smdh.h +++ /dev/null @@ -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); \ No newline at end of file diff --git a/source/core/data/ticket.c b/source/core/data/ticket.c deleted file mode 100644 index 33a517e..0000000 --- a/source/core/data/ticket.c +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/source/core/data/ticket.h b/source/core/data/ticket.h deleted file mode 100644 index 898b2b4..0000000 --- a/source/core/data/ticket.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -Result ticket_get_title_id(u64* titleId, u8* ticket, size_t size); \ No newline at end of file diff --git a/source/core/data/tmd.c b/source/core/data/tmd.c deleted file mode 100644 index 2c25c2e..0000000 --- a/source/core/data/tmd.c +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/source/core/data/tmd.h b/source/core/data/tmd.h deleted file mode 100644 index 020e753..0000000 --- a/source/core/data/tmd.h +++ /dev/null @@ -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); \ No newline at end of file diff --git a/source/core/default.v.pica b/source/core/default.v.pica deleted file mode 100644 index 01e87f2..0000000 --- a/source/core/default.v.pica +++ /dev/null @@ -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 \ No newline at end of file diff --git a/source/core/error.c b/source/core/error.c deleted file mode 100644 index 58a9de7..0000000 --- a/source/core/error.c +++ /dev/null @@ -1,124 +0,0 @@ -#include -#include -#include -#include - -#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); -} \ No newline at end of file diff --git a/source/core/error.h b/source/core/error.h deleted file mode 100644 index 712293b..0000000 --- a/source/core/error.h +++ /dev/null @@ -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, ...); \ No newline at end of file diff --git a/source/core/fs.c b/source/core/fs.c deleted file mode 100644 index d68f3c5..0000000 --- a/source/core/fs.c +++ /dev/null @@ -1,229 +0,0 @@ -#include -#include -#include - -#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); -} \ No newline at end of file diff --git a/source/core/fs.h b/source/core/fs.h deleted file mode 100644 index 2356794..0000000 --- a/source/core/fs.h +++ /dev/null @@ -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); \ No newline at end of file diff --git a/source/core/http.c b/source/core/http.c deleted file mode 100644 index af51a18..0000000 --- a/source/core/http.c +++ /dev/null @@ -1,502 +0,0 @@ -#include -#include -#include - -#include <3ds.h> -#include -#include -#include - -#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(®ion); - - 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; -} \ No newline at end of file diff --git a/source/core/http.h b/source/core/http.h deleted file mode 100644 index 9facaaf..0000000 --- a/source/core/http.h +++ /dev/null @@ -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); \ No newline at end of file diff --git a/source/core/linkedlist.c b/source/core/linkedlist.c deleted file mode 100644 index e0113c8..0000000 --- a/source/core/linkedlist.c +++ /dev/null @@ -1,279 +0,0 @@ -#include -#include - -#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; -} diff --git a/source/core/linkedlist.h b/source/core/linkedlist.h deleted file mode 100644 index 7b10784..0000000 --- a/source/core/linkedlist.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include - -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); \ No newline at end of file diff --git a/source/core/screen.c b/source/core/screen.c deleted file mode 100644 index 82ec0d5..0000000 --- a/source/core/screen.c +++ /dev/null @@ -1,760 +0,0 @@ -#include -#include -#include -#include - -#include <3ds.h> -#include - -#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); -} diff --git a/source/core/screen.h b/source/core/screen.h deleted file mode 100644 index 2e06d99..0000000 --- a/source/core/screen.h +++ /dev/null @@ -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); \ No newline at end of file diff --git a/source/core/spi.c b/source/core/spi.c deleted file mode 100644 index aaabe6a..0000000 --- a/source/core/spi.c +++ /dev/null @@ -1,495 +0,0 @@ -#include -#include - -#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 - */ - -#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); -} \ No newline at end of file diff --git a/source/core/spi.h b/source/core/spi.h deleted file mode 100644 index 738f25a..0000000 --- a/source/core/spi.h +++ /dev/null @@ -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); \ No newline at end of file diff --git a/source/core/stringutil.c b/source/core/stringutil.c deleted file mode 100644 index e3d273a..0000000 --- a/source/core/stringutil.c +++ /dev/null @@ -1,104 +0,0 @@ -#include - -#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'; -} \ No newline at end of file diff --git a/source/core/stringutil.h b/source/core/stringutil.h deleted file mode 100644 index 197b12b..0000000 --- a/source/core/stringutil.h +++ /dev/null @@ -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); \ No newline at end of file diff --git a/source/core/task/capturecam.c b/source/core/task/capturecam.c deleted file mode 100644 index 98b1d88..0000000 --- a/source/core/task/capturecam.c +++ /dev/null @@ -1,152 +0,0 @@ -#include -#include - -#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; -} \ No newline at end of file diff --git a/source/core/task/capturecam.h b/source/core/task/capturecam.h deleted file mode 100644 index 23e2a05..0000000 --- a/source/core/task/capturecam.h +++ /dev/null @@ -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); \ No newline at end of file diff --git a/source/core/task/dataop.c b/source/core/task/dataop.c deleted file mode 100644 index f6c62ab..0000000 --- a/source/core/task/dataop.c +++ /dev/null @@ -1,338 +0,0 @@ -#include -#include - -#include <3ds.h> -#include - -#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; -} \ No newline at end of file diff --git a/source/core/task/dataop.h b/source/core/task/dataop.h deleted file mode 100644 index 9b033f3..0000000 --- a/source/core/task/dataop.h +++ /dev/null @@ -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); \ No newline at end of file diff --git a/source/core/task/task.c b/source/core/task/task.c deleted file mode 100644 index 35fa855..0000000 --- a/source/core/task/task.c +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/source/core/task/task.h b/source/core/task/task.h deleted file mode 100644 index a0ed750..0000000 --- a/source/core/task/task.h +++ /dev/null @@ -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" \ No newline at end of file diff --git a/source/core/ui/error.c b/source/core/ui/error.c deleted file mode 100644 index 61035c3..0000000 --- a/source/core/ui/error.c +++ /dev/null @@ -1,785 +0,0 @@ -#include -#include -#include -#include - -#include <3ds.h> -#include - -#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 ""; - } -} - -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 ""; - } -} - -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 ""; - } -} - -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 ""; - } -} - -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); -} diff --git a/source/core/ui/error.h b/source/core/ui/error.h deleted file mode 100644 index d089f5f..0000000 --- a/source/core/ui/error.h +++ /dev/null @@ -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, ...); \ No newline at end of file diff --git a/source/core/ui/info.c b/source/core/ui/info.c deleted file mode 100644 index 8b2f7d2..0000000 --- a/source/core/ui/info.c +++ /dev/null @@ -1,104 +0,0 @@ -#include -#include - -#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); - } -} \ No newline at end of file diff --git a/source/core/ui/info.h b/source/core/ui/info.h deleted file mode 100644 index ed325d0..0000000 --- a/source/core/ui/info.h +++ /dev/null @@ -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); \ No newline at end of file diff --git a/source/core/ui/kbd.c b/source/core/ui/kbd.c deleted file mode 100644 index 9288e00..0000000 --- a/source/core/ui/kbd.c +++ /dev/null @@ -1,81 +0,0 @@ -#include - -#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; -} \ No newline at end of file diff --git a/source/core/ui/kbd.h b/source/core/ui/kbd.h deleted file mode 100644 index dc5ae7e..0000000 --- a/source/core/ui/kbd.h +++ /dev/null @@ -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)); \ No newline at end of file diff --git a/source/core/ui/list.c b/source/core/ui/list.c deleted file mode 100644 index 695222c..0000000 --- a/source/core/ui/list.c +++ /dev/null @@ -1,306 +0,0 @@ -#include - -#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); - } -} \ No newline at end of file diff --git a/source/core/ui/list.h b/source/core/ui/list.h deleted file mode 100644 index d478aaa..0000000 --- a/source/core/ui/list.h +++ /dev/null @@ -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); \ No newline at end of file diff --git a/source/core/ui/prompt.c b/source/core/ui/prompt.c deleted file mode 100644 index c7d533e..0000000 --- a/source/core/ui/prompt.c +++ /dev/null @@ -1,203 +0,0 @@ -#include -#include -#include - -#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); -} diff --git a/source/core/ui/prompt.h b/source/core/ui/prompt.h deleted file mode 100644 index 84afda2..0000000 --- a/source/core/ui/prompt.h +++ /dev/null @@ -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)); \ No newline at end of file diff --git a/source/core/ui/ui.c b/source/core/ui/ui.c deleted file mode 100644 index 5a4c4cc..0000000 --- a/source/core/ui/ui.c +++ /dev/null @@ -1,435 +0,0 @@ -#include -#include -#include - -#include <3ds.h> -#include - -#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"; -} \ No newline at end of file diff --git a/source/core/ui/ui.h b/source/core/ui/ui.h deleted file mode 100644 index b8397ee..0000000 --- a/source/core/ui/ui.h +++ /dev/null @@ -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" \ No newline at end of file diff --git a/source/fbi/.DS_Store b/source/fbi/.DS_Store deleted file mode 100644 index ebf35ef..0000000 Binary files a/source/fbi/.DS_Store and /dev/null differ diff --git a/source/fbi/action/action.h b/source/fbi/action/action.h deleted file mode 100644 index 29811d8..0000000 --- a/source/fbi/action/action.h +++ /dev/null @@ -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)); \ No newline at end of file diff --git a/source/fbi/action/browsebossextsavedata.c b/source/fbi/action/browsebossextsavedata.c deleted file mode 100644 index 28dc397..0000000 --- a/source/fbi/action/browsebossextsavedata.c +++ /dev/null @@ -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))); -} \ No newline at end of file diff --git a/source/fbi/action/browsesystemsavedata.c b/source/fbi/action/browsesystemsavedata.c deleted file mode 100644 index 7869ce9..0000000 --- a/source/fbi/action/browsesystemsavedata.c +++ /dev/null @@ -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))); -} \ No newline at end of file diff --git a/source/fbi/action/browsetitlesavedata.c b/source/fbi/action/browsetitlesavedata.c deleted file mode 100644 index baf3205..0000000 --- a/source/fbi/action/browsetitlesavedata.c +++ /dev/null @@ -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))); -} \ No newline at end of file diff --git a/source/fbi/action/browseuserextsavedata.c b/source/fbi/action/browseuserextsavedata.c deleted file mode 100644 index a3bec6c..0000000 --- a/source/fbi/action/browseuserextsavedata.c +++ /dev/null @@ -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))); -} \ No newline at end of file diff --git a/source/fbi/action/deletecontents.c b/source/fbi/action/deletecontents.c deleted file mode 100644 index 34491e5..0000000 --- a/source/fbi/action/deletecontents.c +++ /dev/null @@ -1,268 +0,0 @@ -#include -#include -#include - -#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); -} diff --git a/source/fbi/action/deleteextsavedata.c b/source/fbi/action/deleteextsavedata.c deleted file mode 100644 index c502f38..0000000 --- a/source/fbi/action/deleteextsavedata.c +++ /dev/null @@ -1,61 +0,0 @@ -#include - -#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); -} diff --git a/source/fbi/action/deletependingtitles.c b/source/fbi/action/deletependingtitles.c deleted file mode 100644 index dfd63d6..0000000 --- a/source/fbi/action/deletependingtitles.c +++ /dev/null @@ -1,229 +0,0 @@ -#include -#include -#include - -#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); -} diff --git a/source/fbi/action/deletesecurevalue.c b/source/fbi/action/deletesecurevalue.c deleted file mode 100644 index fd7f1c4..0000000 --- a/source/fbi/action/deletesecurevalue.c +++ /dev/null @@ -1,35 +0,0 @@ -#include - -#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, ¶m, 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); -} diff --git a/source/fbi/action/deletesystemsavedata.c b/source/fbi/action/deletesystemsavedata.c deleted file mode 100644 index a3efa7b..0000000 --- a/source/fbi/action/deletesystemsavedata.c +++ /dev/null @@ -1,62 +0,0 @@ -#include - -#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_system_save_data_data; - -static void action_delete_system_save_data_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - task_draw_system_save_data_info(view, ((delete_system_save_data_data*) data)->selected->data, x1, y1, x2, y2); -} - -static void action_delete_system_save_data_update(ui_view* view, void* data, float* progress, char* text) { - delete_system_save_data_data* deleteData = (delete_system_save_data_data*) data; - - system_save_data_info* info = (system_save_data_info*) deleteData->selected->data; - - FS_SystemSaveDataInfo sysInfo = {.mediaType = MEDIATYPE_NAND, .saveId = info->systemSaveDataId}; - Result res = FSUSER_DeleteSystemSaveData(sysInfo); - - ui_pop(); - info_destroy(view); - - if(R_FAILED(res)) { - error_display_res(info, task_draw_system_save_data_info, res, "无法删除系统数据."); - } else { - linked_list_remove(deleteData->items, deleteData->selected); - task_free_system_save_data(deleteData->selected); - - prompt_display_notify("成功", "已删除.", COLOR_TEXT, NULL, NULL, NULL); - } - - free(data); -} - -static void action_delete_system_save_data_onresponse(ui_view* view, void* data, u32 response) { - if(response == PROMPT_YES) { - info_display("正在删除", "", false, data, action_delete_system_save_data_update, action_delete_system_save_data_draw_top); - } else { - free(data); - } -} - -void action_delete_system_save_data(linked_list* items, list_item* selected) { - delete_system_save_data_data* data = (delete_system_save_data_data*) calloc(1, sizeof(delete_system_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_system_save_data_draw_top, action_delete_system_save_data_onresponse); -} diff --git a/source/fbi/action/deletetickets.c b/source/fbi/action/deletetickets.c deleted file mode 100644 index 6bffb00..0000000 --- a/source/fbi/action/deletetickets.c +++ /dev/null @@ -1,235 +0,0 @@ -#include -#include - -#include <3ds.h> - -#include "action.h" -#include "../resources.h" -#include "../task/uitask.h" -#include "../../core/core.h" - -typedef struct { - linked_list* items; - bool unused; - - linked_list contents; - - data_op_data deleteInfo; -} delete_tickets_data; - -static void action_delete_tickets_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - delete_tickets_data* deleteData = (delete_tickets_data*) data; - - u32 curr = deleteData->deleteInfo.processed; - if(curr < deleteData->deleteInfo.total) { - task_draw_ticket_info(view, ((list_item*) linked_list_get(&deleteData->contents, curr))->data, x1, y1, x2, y2); - } -} - -static Result action_delete_tickets_delete(void* data, u32 index) { - delete_tickets_data* deleteData = (delete_tickets_data*) data; - - Result res = 0; - - u64 titleId = ((ticket_info*) ((list_item*) linked_list_get(&deleteData->contents, index))->data)->titleId; - if(R_SUCCEEDED(res = AM_DeleteTicket(titleId))) { - 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); - ticket_info* currInfo = (ticket_info*) item->data; - - if(currInfo->titleId == titleId) { - linked_list_iter_remove(&iter); - task_free_ticket(item); - } - } - } - - return res; -} - -static Result action_delete_tickets_suspend(void* data, u32 index) { - return 0; -} - -static Result action_delete_tickets_restore(void* data, u32 index) { - return 0; -} - -static bool action_delete_tickets_error(void* data, u32 index, Result res, ui_view** errorView) { - *errorView = error_display_res(data, action_delete_tickets_draw_top, res, "无法删除应用引导表."); - return true; -} - -static void action_delete_tickets_free_data(delete_tickets_data* data) { - if(data->unused) { - task_clear_tickets(&data->contents); - } - - linked_list_destroy(&data->contents); - free(data); -} - -static void action_delete_tickets_update(ui_view* view, void* data, float* progress, char* text) { - delete_tickets_data* deleteData = (delete_tickets_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_tickets_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_tickets_onresponse(ui_view* view, void* data, u32 response) { - delete_tickets_data* deleteData = (delete_tickets_data*) data; - - if(response == PROMPT_YES) { - Result res = task_data_op(&deleteData->deleteInfo); - if(R_SUCCEEDED(res)) { - info_display("正在删除", "按 B 取消.", true, data, action_delete_tickets_update, action_delete_tickets_draw_top); - } else { - error_display_res(NULL, NULL, res, "无法启动删除操作."); - - action_delete_tickets_free_data(deleteData); - } - } else { - action_delete_tickets_free_data(deleteData); - } -} - -typedef struct { - delete_tickets_data* deleteData; - - const char* message; - - populate_tickets_data popData; -} delete_tickets_loading_data; - -static void action_delete_tickets_loading_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - action_delete_tickets_draw_top(view, ((delete_tickets_loading_data*) data)->deleteData, x1, y1, x2, y2); -} - -static void action_delete_tickets_loading_update(ui_view* view, void* data, float* progress, char* text) { - delete_tickets_loading_data* loadingData = (delete_tickets_loading_data*) data; - - if(loadingData->popData.finished) { - ui_pop(); - info_destroy(view); - - if(R_SUCCEEDED(loadingData->popData.result)) { - linked_list_iter iter; - linked_list_iterate(&loadingData->deleteData->contents, &iter); - while(linked_list_iter_has_next(&iter)) { - list_item* item = (list_item*) linked_list_iter_next(&iter); - ticket_info* info = (ticket_info*) item->data; - - if(info->inUse) { - linked_list_iter_remove(&iter); - task_free_ticket(item); - } - } - - 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_tickets_draw_top, action_delete_tickets_onresponse); - } else { - error_display_res(NULL, NULL, loadingData->popData.result, "无法填充应用引导表列表."); - - action_delete_tickets_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_tickets_internal(linked_list* items, list_item* selected, const char* message, bool unused) { - delete_tickets_data* data = (delete_tickets_data*) calloc(1, sizeof(delete_tickets_data)); - if(data == NULL) { - error_display(NULL, NULL, "无法分配删除的数据."); - - return; - } - - data->items = items; - data->unused = unused; - - data->deleteInfo.data = data; - - data->deleteInfo.op = DATAOP_DELETE; - - data->deleteInfo.delete = action_delete_tickets_delete; - - data->deleteInfo.suspend = action_delete_tickets_suspend; - data->deleteInfo.restore = action_delete_tickets_restore; - - data->deleteInfo.error = action_delete_tickets_error; - - data->deleteInfo.finished = false; - - linked_list_init(&data->contents); - - if(unused) { - delete_tickets_loading_data* loadingData = (delete_tickets_loading_data*) calloc(1, sizeof(delete_tickets_loading_data)); - if(loadingData == NULL) { - error_display(NULL, NULL, "无法分配加载的数据."); - - action_delete_tickets_free_data(data); - return; - } - - loadingData->deleteData = data; - loadingData->message = message; - - loadingData->popData.items = &data->contents; - - Result listRes = task_populate_tickets(&loadingData->popData); - if(R_FAILED(listRes)) { - error_display_res(NULL, NULL, listRes, "无法启动应用引导表列表填充."); - - free(loadingData); - action_delete_tickets_free_data(data); - return; - } - - info_display("正在加载", "按 B 取消.", false, loadingData, action_delete_tickets_loading_update, action_delete_tickets_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_tickets_draw_top, action_delete_tickets_onresponse); - } -} - -void action_delete_ticket(linked_list* items, list_item* selected) { - action_delete_tickets_internal(items, selected, "删除所选应用引导表?", false); -} - -void action_delete_tickets_unused(linked_list* items, list_item* selected) { - action_delete_tickets_internal(items, selected, "删除所有未使用的应用引导表?", true); -} diff --git a/source/fbi/action/deletetitle.c b/source/fbi/action/deletetitle.c deleted file mode 100644 index 5eb2beb..0000000 --- a/source/fbi/action/deletetitle.c +++ /dev/null @@ -1,75 +0,0 @@ -#include - -#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; - bool ticket; -} delete_title_data; - -static void action_delete_title_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - task_draw_title_info(view, ((delete_title_data*) data)->selected->data, x1, y1, x2, y2); -} - -static void action_delete_title_update(ui_view* view, void* data, float* progress, char* text) { - delete_title_data* deleteData = (delete_title_data*) data; - - title_info* info = (title_info*) deleteData->selected->data; - - Result res = 0; - - if(R_SUCCEEDED(res = AM_DeleteTitle(info->mediaType, info->titleId)) && deleteData->ticket) { - res = AM_DeleteTicket(info->titleId); - } - - ui_pop(); - info_destroy(view); - - if(R_FAILED(res)) { - error_display_res(info, task_draw_title_info, res, "无法删除应用."); - } else { - linked_list_remove(deleteData->items, deleteData->selected); - task_free_title(deleteData->selected); - - prompt_display_notify("成功", "已删除.", COLOR_TEXT, NULL, NULL, NULL); - } - - free(data); -} - -static void action_delete_title_onresponse(ui_view* view, void* data, u32 response) { - if(response == PROMPT_YES) { - info_display("正在删除", "", false, data, action_delete_title_update, action_delete_title_draw_top); - } else { - free(data); - } -} - -static void action_delete_title_internal(linked_list* items, list_item* selected, const char* message, bool ticket) { - delete_title_data* data = (delete_title_data*) calloc(1, sizeof(delete_title_data)); - if(data == NULL) { - error_display(NULL, NULL, "无法分配删除应用的数据."); - - return; - } - - data->items = items; - data->selected = selected; - data->ticket = ticket; - - prompt_display_yes_no("确认", message, COLOR_TEXT, data, action_delete_title_draw_top, action_delete_title_onresponse); -} - -void action_delete_title(linked_list* items, list_item* selected) { - action_delete_title_internal(items, selected, "删除所选应用?", false); -} - -void action_delete_title_ticket(linked_list* items, list_item* selected) { - action_delete_title_internal(items, selected, "删除所选的应用和应用引导表?", true); -} diff --git a/source/fbi/action/erasetwlsave.c b/source/fbi/action/erasetwlsave.c deleted file mode 100644 index 01eb5c6..0000000 --- a/source/fbi/action/erasetwlsave.c +++ /dev/null @@ -1,168 +0,0 @@ -#include -#include -#include - -#include <3ds.h> - -#include "action.h" -#include "../resources.h" -#include "../task/uitask.h" -#include "../../core/core.h" - -typedef struct { - title_info* title; - - data_op_data eraseInfo; -} erase_twl_save_data; - -static void action_erase_twl_save_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - task_draw_title_info(view, ((erase_twl_save_data*) data)->title, x1, y1, x2, y2); -} - -static Result action_erase_twl_save_is_src_directory(void* data, u32 index, bool* isDirectory) { - *isDirectory = false; - return 0; -} - -static Result action_erase_twl_save_make_dst_directory(void* data, u32 index) { - return 0; -} - -static Result action_erase_twl_save_open_src(void* data, u32 index, u32* handle) { - return 0; -} - -static Result action_erase_twl_save_close_src(void* data, u32 index, bool succeeded, u32 handle) { - return 0; -} - -static Result action_erase_twl_save_get_src_size(void* data, u32 handle, u64* size) { - Result res = 0; - - u32 saveSize = 0; - if(R_SUCCEEDED(res = spi_init_card()) && R_SUCCEEDED(res = spi_get_save_size(&saveSize)) && R_SUCCEEDED(res = spi_deinit_card())) { - *size = saveSize; - } - - return res; -} - -static Result action_erase_twl_save_read_src(void* data, u32 handle, u32* bytesRead, void* buffer, u64 offset, u32 size) { - memset(buffer, 0, size); - *bytesRead = size; - - return 0; -} - -static Result action_erase_twl_save_open_dst(void* data, u32 index, void* initialReadBlock, u64 size, u32* handle) { - return spi_init_card(); -} - -static Result action_erase_twl_save_close_dst(void* data, u32 index, bool succeeded, u32 handle) { - return spi_deinit_card(); -} - -static Result action_erase_twl_save_write_dst(void* data, u32 handle, u32* bytesWritten, void* buffer, u64 offset, u32 size) { - return spi_write_save(bytesWritten, buffer, (u32) offset, size); -} - -static Result action_erase_twl_save_suspend(void* data, u32 index) { - return 0; -} - -static Result action_erase_twl_save_restore(void* data, u32 index) { - return 0; -} - -static bool action_erase_twl_save_error(void* data, u32 index, Result res, ui_view** errorView) { - *errorView = error_display_res(((erase_twl_save_data*) data)->title, task_draw_title_info, res, "无法清除数据."); - return true; -} - -static void action_erase_twl_save_update(ui_view* view, void* data, float* progress, char* text) { - erase_twl_save_data* eraseData = (erase_twl_save_data*) data; - - if(eraseData->eraseInfo.finished) { - ui_pop(); - info_destroy(view); - - if(R_SUCCEEDED(eraseData->eraseInfo.result)) { - prompt_display_notify("成功", "已清除.", COLOR_TEXT, eraseData->title, task_draw_title_info, NULL); - } - - free(data); - - return; - } - - if(hidKeysDown() & KEY_B) { - svcSignalEvent(eraseData->eraseInfo.cancelEvent); - } - - *progress = eraseData->eraseInfo.currTotal != 0 ? (float) ((double) eraseData->eraseInfo.currProcessed / (double) eraseData->eraseInfo.currTotal) : 0; - snprintf(text, PROGRESS_TEXT_MAX, "%.2f %s / %.2f %s\n%.2f %s/s, ETA %s", - ui_get_display_size(eraseData->eraseInfo.currProcessed), - ui_get_display_size_units(eraseData->eraseInfo.currProcessed), - ui_get_display_size(eraseData->eraseInfo.currTotal), - ui_get_display_size_units(eraseData->eraseInfo.currTotal), - ui_get_display_size(eraseData->eraseInfo.bytesPerSecond), - ui_get_display_size_units(eraseData->eraseInfo.bytesPerSecond), - ui_get_display_eta(eraseData->eraseInfo.estimatedRemainingSeconds)); -} - -static void action_erase_twl_save_onresponse(ui_view* view, void* data, u32 response) { - if(response == PROMPT_YES) { - erase_twl_save_data* eraseData = (erase_twl_save_data*) data; - - Result res = task_data_op(&eraseData->eraseInfo); - if(R_SUCCEEDED(res)) { - info_display("正在清除", "按 B 取消.", true, data, action_erase_twl_save_update, action_erase_twl_save_draw_top); - } else { - error_display_res(eraseData->title, task_draw_title_info, res, "无法启动清除数据."); - free(data); - } - } else { - free(data); - } -} - -void action_erase_twl_save(linked_list* items, list_item* selected) { - erase_twl_save_data* data = (erase_twl_save_data*) calloc(1, sizeof(erase_twl_save_data)); - if(data == NULL) { - error_display(NULL, NULL, "无法分配清除 TWL 数据的数据."); - - return; - } - - data->title = (title_info*) selected->data; - - data->eraseInfo.data = data; - - data->eraseInfo.op = DATAOP_COPY; - - data->eraseInfo.bufferSize = 16 * 1024; - data->eraseInfo.copyEmpty = true; - - data->eraseInfo.total = 1; - - data->eraseInfo.isSrcDirectory = action_erase_twl_save_is_src_directory; - data->eraseInfo.makeDstDirectory = action_erase_twl_save_make_dst_directory; - - data->eraseInfo.openSrc = action_erase_twl_save_open_src; - data->eraseInfo.closeSrc = action_erase_twl_save_close_src; - data->eraseInfo.getSrcSize = action_erase_twl_save_get_src_size; - data->eraseInfo.readSrc = action_erase_twl_save_read_src; - - data->eraseInfo.openDst = action_erase_twl_save_open_dst; - data->eraseInfo.closeDst = action_erase_twl_save_close_dst; - data->eraseInfo.writeDst = action_erase_twl_save_write_dst; - - data->eraseInfo.suspend = action_erase_twl_save_suspend; - data->eraseInfo.restore = action_erase_twl_save_restore; - - data->eraseInfo.error = action_erase_twl_save_error; - - data->eraseInfo.finished = true; - - prompt_display_yes_no("确认", "清除所选应用的数据?", COLOR_TEXT, data, action_erase_twl_save_draw_top, action_erase_twl_save_onresponse); -} diff --git a/source/fbi/action/exportsecurevalue.c b/source/fbi/action/exportsecurevalue.c deleted file mode 100644 index e0952c4..0000000 --- a/source/fbi/action/exportsecurevalue.c +++ /dev/null @@ -1,70 +0,0 @@ -#include - -#include <3ds.h> - -#include "action.h" -#include "../resources.h" -#include "../task/uitask.h" -#include "../../core/core.h" - -static void action_export_secure_value_update(ui_view* view, void* data, float* progress, char* text) { - title_info* info = (title_info*) data; - - Result res = 0; - - bool exists = false; - u64 value = 0; - if(R_SUCCEEDED(res = FSUSER_GetSaveDataSecureValue(&exists, &value, SECUREVALUE_SLOT_SD, (u32) ((info->titleId >> 8) & 0xFFFFF), (u8) (info->titleId & 0xFF)))) { - if(!exists) { - ui_pop(); - info_destroy(view); - - prompt_display_notify("失败", "未设置安全值.", COLOR_TEXT, info, task_draw_title_info, NULL); - - return; - } - - FS_Archive sdmcArchive = 0; - if(R_SUCCEEDED(res = FSUSER_OpenArchive(&sdmcArchive, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")))) { - if(R_SUCCEEDED(res = fs_ensure_dir(sdmcArchive, "/fbi/")) && R_SUCCEEDED(res = fs_ensure_dir(sdmcArchive, "/fbi/securevalue/"))) { - char pathBuf[64]; - snprintf(pathBuf, 64, "/fbi/securevalue/%016llX.dat", info->titleId); - - FS_Path* fsPath = fs_make_path_utf8(pathBuf); - if(fsPath != NULL) { - Handle fileHandle = 0; - if(R_SUCCEEDED(res = FSUSER_OpenFile(&fileHandle, sdmcArchive, *fsPath, FS_OPEN_WRITE | FS_OPEN_CREATE, 0))) { - u32 bytesWritten = 0; - res = FSFILE_Write(fileHandle, &bytesWritten, 0, &value, sizeof(u64), FS_WRITE_FLUSH | FS_WRITE_UPDATE_TIME); - FSFILE_Close(fileHandle); - } - - fs_free_path_utf8(fsPath); - } else { - res = R_APP_OUT_OF_MEMORY; - } - } - - FSUSER_CloseArchive(sdmcArchive); - } - } - - ui_pop(); - info_destroy(view); - - if(R_SUCCEEDED(res)) { - prompt_display_notify("成功", "已导出.", COLOR_TEXT, info, task_draw_title_info, NULL); - } else { - error_display_res(info, task_draw_title_info, res, "无法导出安全值."); - } -} - -static void action_export_secure_value_onresponse(ui_view* view, void* data, u32 response) { - if(response == PROMPT_YES) { - info_display("正在导出", "", false, data, action_export_secure_value_update, task_draw_title_info); - } -} - -void action_export_secure_value(linked_list* items, list_item* selected) { - prompt_display_yes_no("确认", "导出所选应用的安全值?", COLOR_TEXT, selected->data, task_draw_title_info, action_export_secure_value_onresponse); -} diff --git a/source/fbi/action/exporttwlsave.c b/source/fbi/action/exporttwlsave.c deleted file mode 100644 index 202e7fe..0000000 --- a/source/fbi/action/exporttwlsave.c +++ /dev/null @@ -1,190 +0,0 @@ -#include -#include - -#include <3ds.h> - -#include "action.h" -#include "../resources.h" -#include "../task/uitask.h" -#include "../../core/core.h" - -typedef struct { - title_info* title; - - data_op_data exportInfo; -} export_twl_save_data; - -static void action_export_twl_save_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - task_draw_title_info(view, ((export_twl_save_data*) data)->title, x1, y1, x2, y2); -} - -static Result action_export_twl_save_is_src_directory(void* data, u32 index, bool* isDirectory) { - *isDirectory = false; - return 0; -} - -static Result action_export_twl_save_make_dst_directory(void* data, u32 index) { - return 0; -} - -static Result action_export_twl_save_open_src(void* data, u32 index, u32* handle) { - return spi_init_card(); -} - -static Result action_export_twl_save_close_src(void* data, u32 index, bool succeeded, u32 handle) { - return spi_deinit_card(); -} - -static Result action_export_twl_save_get_src_size(void* data, u32 handle, u64* size) { - Result res = 0; - - u32 saveSize = 0; - if(R_SUCCEEDED(res = spi_get_save_size(&saveSize))) { - *size = saveSize; - } - - return res; -} - -static Result action_export_twl_save_read_src(void* data, u32 handle, u32* bytesRead, void* buffer, u64 offset, u32 size) { - return spi_read_save(bytesRead, buffer, (u32) offset, size); -} - -static Result action_export_twl_save_open_dst(void* data, u32 index, void* initialReadBlock, u64 size, u32* handle) { - export_twl_save_data* exportData = (export_twl_save_data*) data; - - Result res = 0; - - FS_Archive sdmcArchive = 0; - if(R_SUCCEEDED(res = FSUSER_OpenArchive(&sdmcArchive, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")))) { - if(R_SUCCEEDED(res = fs_ensure_dir(sdmcArchive, "/fbi/")) && R_SUCCEEDED(res = fs_ensure_dir(sdmcArchive, "/fbi/save/"))) { - char gameName[0x10] = {'\0'}; - string_escape_file_name(gameName, exportData->title->productCode, sizeof(gameName)); - - char path[FILE_PATH_MAX]; - snprintf(path, sizeof(path), "/fbi/save/%s.sav", gameName); - - FS_Path* fsPath = fs_make_path_utf8(path); - if(fsPath != NULL) { - res = FSUSER_OpenFileDirectly(handle, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), *fsPath, FS_OPEN_WRITE | FS_OPEN_CREATE, 0); - - fs_free_path_utf8(fsPath); - } else { - res = R_APP_OUT_OF_MEMORY; - } - } - - FSUSER_CloseArchive(sdmcArchive); - } - - return res; -} - -static Result action_export_twl_save_close_dst(void* data, u32 index, bool succeeded, u32 handle) { - return FSFILE_Close(handle); -} - -static Result action_export_twl_save_write_dst(void* data, u32 handle, u32* bytesWritten, void* buffer, u64 offset, u32 size) { - return FSFILE_Write(handle, bytesWritten, offset, buffer, size, 0); -} - -static Result action_export_twl_save_suspend(void* data, u32 index) { - return 0; -} - -static Result action_export_twl_save_restore(void* data, u32 index) { - return 0; -} - -static bool action_export_twl_save_error(void* data, u32 index, Result res, ui_view** errorView) { - *errorView = error_display_res(((export_twl_save_data*) data)->title, task_draw_title_info, res, "无法导出数据."); - return true; -} - -static void action_export_twl_save_update(ui_view* view, void* data, float* progress, char* text) { - export_twl_save_data* exportData = (export_twl_save_data*) data; - - if(exportData->exportInfo.finished) { - ui_pop(); - info_destroy(view); - - if(R_SUCCEEDED(exportData->exportInfo.result)) { - prompt_display_notify("成功", "已导出.", COLOR_TEXT, exportData->title, task_draw_title_info, NULL); - } - - free(data); - - return; - } - - if(hidKeysDown() & KEY_B) { - svcSignalEvent(exportData->exportInfo.cancelEvent); - } - - *progress = exportData->exportInfo.currTotal != 0 ? (float) ((double) exportData->exportInfo.currProcessed / (double) exportData->exportInfo.currTotal) : 0; - snprintf(text, PROGRESS_TEXT_MAX, "%.2f %s / %.2f %s\n%.2f %s/s, ETA %s", - ui_get_display_size(exportData->exportInfo.currProcessed), - ui_get_display_size_units(exportData->exportInfo.currProcessed), - ui_get_display_size(exportData->exportInfo.currTotal), - ui_get_display_size_units(exportData->exportInfo.currTotal), - ui_get_display_size(exportData->exportInfo.bytesPerSecond), - ui_get_display_size_units(exportData->exportInfo.bytesPerSecond), - ui_get_display_eta(exportData->exportInfo.estimatedRemainingSeconds)); -} - -static void action_export_twl_save_onresponse(ui_view* view, void* data, u32 response) { - if(response == PROMPT_YES) { - export_twl_save_data* exportData = (export_twl_save_data*) data; - - Result res = task_data_op(&exportData->exportInfo); - if(R_SUCCEEDED(res)) { - info_display("正在导出", "按 B 取消.", true, data, action_export_twl_save_update, action_export_twl_save_draw_top); - } else { - error_display_res(exportData->title, task_draw_title_info, res, "无法启动导出数据."); - free(data); - } - } else { - free(data); - } -} - -void action_export_twl_save(linked_list* items, list_item* selected) { - export_twl_save_data* data = (export_twl_save_data*) calloc(1, sizeof(export_twl_save_data)); - if(data == NULL) { - error_display(NULL, NULL, "无法分配导出 TWL 数据的数据."); - - return; - } - - data->title = (title_info*) selected->data; - - data->exportInfo.data = data; - - data->exportInfo.op = DATAOP_COPY; - - data->exportInfo.bufferSize = 128 * 1024; - data->exportInfo.copyEmpty = true; - - data->exportInfo.total = 1; - - data->exportInfo.isSrcDirectory = action_export_twl_save_is_src_directory; - data->exportInfo.makeDstDirectory = action_export_twl_save_make_dst_directory; - - data->exportInfo.openSrc = action_export_twl_save_open_src; - data->exportInfo.closeSrc = action_export_twl_save_close_src; - data->exportInfo.getSrcSize = action_export_twl_save_get_src_size; - data->exportInfo.readSrc = action_export_twl_save_read_src; - - data->exportInfo.openDst = action_export_twl_save_open_dst; - data->exportInfo.closeDst = action_export_twl_save_close_dst; - data->exportInfo.writeDst = action_export_twl_save_write_dst; - - data->exportInfo.suspend = action_export_twl_save_suspend; - data->exportInfo.restore = action_export_twl_save_restore; - - data->exportInfo.error = action_export_twl_save_error; - - data->exportInfo.finished = true; - - prompt_display_yes_no("确认", "导出所选应用的数据?", COLOR_TEXT, data, action_export_twl_save_draw_top, action_export_twl_save_onresponse); -} diff --git a/source/fbi/action/extractsmdh.c b/source/fbi/action/extractsmdh.c deleted file mode 100644 index c443bc3..0000000 --- a/source/fbi/action/extractsmdh.c +++ /dev/null @@ -1,78 +0,0 @@ -#include -#include - -#include <3ds.h> - -#include "action.h" -#include "../resources.h" -#include "../task/uitask.h" -#include "../../core/core.h" - -static void action_extract_smdh_update(ui_view* view, void* data, float* progress, char* text) { - title_info* info = (title_info*) data; - - Result res = 0; - - static const u32 filePath[5] = {0x00000000, 0x00000000, 0x00000002, 0x6E6F6369, 0x00000000}; - u32 archivePath[4] = {(u32) (info->titleId & 0xFFFFFFFF), (u32) ((info->titleId >> 32) & 0xFFFFFFFF), info->mediaType, 0x00000000}; - - Handle fileHandle; - if(R_SUCCEEDED(res = FSUSER_OpenFileDirectly(&fileHandle, ARCHIVE_SAVEDATA_AND_CONTENT, - fs_make_path_binary(archivePath, sizeof(archivePath)), - fs_make_path_binary(filePath, sizeof(filePath)), FS_OPEN_READ, 0))) { - SMDH* smdh = (SMDH*) calloc(1, sizeof(SMDH)); - if(smdh != NULL) { - u32 bytesRead = 0; - if(R_SUCCEEDED(res = FSFILE_Read(fileHandle, &bytesRead, 0, smdh, sizeof(SMDH))) && bytesRead == sizeof(SMDH)) { - FS_Archive sdmcArchive = 0; - if(R_SUCCEEDED(res = FSUSER_OpenArchive(&sdmcArchive, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")))) { - if(R_SUCCEEDED(res = fs_ensure_dir(sdmcArchive, "/fbi/")) && R_SUCCEEDED(res = fs_ensure_dir(sdmcArchive, "/fbi/smdh/"))) { - char pathBuf[64]; - snprintf(pathBuf, 64, "/fbi/smdh/%016llX.smdh", info->titleId); - - FS_Path* fsPath = fs_make_path_utf8(pathBuf); - if(fsPath != NULL) { - Handle smdhHandle = 0; - if(R_SUCCEEDED(res = FSUSER_OpenFile(&smdhHandle, sdmcArchive, *fsPath, FS_OPEN_WRITE | FS_OPEN_CREATE, 0))) { - u32 bytesWritten = 0; - res = FSFILE_Write(smdhHandle, &bytesWritten, 0, smdh, sizeof(SMDH), FS_WRITE_FLUSH | FS_WRITE_UPDATE_TIME); - FSFILE_Close(smdhHandle); - } - - fs_free_path_utf8(fsPath); - } else { - res = R_APP_OUT_OF_MEMORY; - } - } - - FSUSER_CloseArchive(sdmcArchive); - } - } - - free(smdh); - } else { - res = R_APP_OUT_OF_MEMORY; - } - - FSFILE_Close(fileHandle); - } - - ui_pop(); - info_destroy(view); - - if(R_SUCCEEDED(res)) { - prompt_display_notify("成功", "已提取.", COLOR_TEXT, info, task_draw_title_info, NULL); - } else { - error_display_res(info, task_draw_title_info, res, "无法提取 SMDH."); - } -} - -static void action_extract_smdh_onresponse(ui_view* view, void* data, u32 response) { - if(response == PROMPT_YES) { - info_display("正在提取", "", false, data, action_extract_smdh_update, task_draw_title_info); - } -} - -void action_extract_smdh(linked_list* items, list_item* selected) { - prompt_display_yes_no("确认", "提取所选应用的 SMDH?", COLOR_TEXT, selected->data, task_draw_title_info, action_extract_smdh_onresponse); -} diff --git a/source/fbi/action/importsecurevalue.c b/source/fbi/action/importsecurevalue.c deleted file mode 100644 index 7be4d1c..0000000 --- a/source/fbi/action/importsecurevalue.c +++ /dev/null @@ -1,54 +0,0 @@ -#include - -#include <3ds.h> - -#include "action.h" -#include "../resources.h" -#include "../task/uitask.h" -#include "../../core/core.h" - -static void action_import_secure_value_update(ui_view* view, void* data, float* progress, char* text) { - title_info* info = (title_info*) data; - - char pathBuf[64]; - snprintf(pathBuf, 64, "/fbi/securevalue/%016llX.dat", info->titleId); - - Result res = 0; - - FS_Path* fsPath = fs_make_path_utf8(pathBuf); - if(fsPath != NULL) { - Handle fileHandle = 0; - if(R_SUCCEEDED(res = FSUSER_OpenFileDirectly(&fileHandle, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), *fsPath, FS_OPEN_READ, 0))) { - u32 bytesRead = 0; - u64 value = 0; - if(R_SUCCEEDED(res = FSFILE_Read(fileHandle, &bytesRead, 0, &value, sizeof(u64)))) { - res = FSUSER_SetSaveDataSecureValue(value, SECUREVALUE_SLOT_SD, (u32) ((info->titleId >> 8) & 0xFFFFF), (u8) (info->titleId & 0xFF)); - } - - FSFILE_Close(fileHandle); - } - - fs_free_path_utf8(fsPath); - } else { - res = R_APP_OUT_OF_MEMORY; - } - - ui_pop(); - info_destroy(view); - - if(R_SUCCEEDED(res)) { - prompt_display_notify("成功", "已导入.", COLOR_TEXT, info, task_draw_title_info, NULL); - } else { - error_display_res(info, task_draw_title_info, res, "无法导入安全值."); - } -} - -static void action_import_secure_value_onresponse(ui_view* view, void* data, u32 response) { - if(response == PROMPT_YES) { - info_display("正在导入", "", false, data, action_import_secure_value_update, task_draw_title_info); - } -} - -void action_import_secure_value(linked_list* items, list_item* selected) { - prompt_display_yes_no("确认", "导入安全值至所选应用?", COLOR_TEXT, selected->data, task_draw_title_info, action_import_secure_value_onresponse); -} diff --git a/source/fbi/action/importseed.c b/source/fbi/action/importseed.c deleted file mode 100644 index a371b4b..0000000 --- a/source/fbi/action/importseed.c +++ /dev/null @@ -1,33 +0,0 @@ -#include - -#include <3ds.h> - -#include "action.h" -#include "../resources.h" -#include "../task/uitask.h" -#include "../../core/core.h" - -static void action_import_seed_update(ui_view* view, void* data, float* progress, char* text) { - title_info* info = (title_info*) data; - - Result res = http_download_seed(info->titleId); - - ui_pop(); - info_destroy(view); - - if(R_SUCCEEDED(res)) { - prompt_display_notify("成功", "已导入.", COLOR_TEXT, info, task_draw_title_info, NULL); - } else { - error_display_res(info, task_draw_title_info, res, "无法导入种子."); - } -} - -static void action_import_seed_onresponse(ui_view* view, void* data, u32 response) { - if(response == PROMPT_YES) { - info_display("正在导入", "", false, data, action_import_seed_update, task_draw_title_info); - } -} - -void action_import_seed(linked_list* items, list_item* selected) { - prompt_display_yes_no("确认", "导入种子至所选应用?", COLOR_TEXT, selected->data, task_draw_title_info, action_import_seed_onresponse); -} diff --git a/source/fbi/action/importtwlsave.c b/source/fbi/action/importtwlsave.c deleted file mode 100644 index ef43dd5..0000000 --- a/source/fbi/action/importtwlsave.c +++ /dev/null @@ -1,176 +0,0 @@ -#include -#include - -#include <3ds.h> - -#include "action.h" -#include "../resources.h" -#include "../task/uitask.h" -#include "../../core/core.h" - -typedef struct { - title_info* title; - - data_op_data importInfo; -} import_twl_save_data; - -static void action_import_twl_save_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - task_draw_title_info(view, ((import_twl_save_data*) data)->title, x1, y1, x2, y2); -} - -static Result action_import_twl_save_is_src_directory(void* data, u32 index, bool* isDirectory) { - *isDirectory = false; - return 0; -} - -static Result action_import_twl_save_make_dst_directory(void* data, u32 index) { - return 0; -} - -static Result action_import_twl_save_open_src(void* data, u32 index, u32* handle) { - import_twl_save_data* importData = (import_twl_save_data*) data; - - Result res = 0; - - char gameName[0x10] = {'\0'}; - string_escape_file_name(gameName, importData->title->productCode, sizeof(gameName)); - - char path[FILE_PATH_MAX]; - snprintf(path, sizeof(path), "/fbi/save/%s.sav", gameName); - - FS_Path* fsPath = fs_make_path_utf8(path); - if(fsPath != NULL) { - res = FSUSER_OpenFileDirectly(handle, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), *fsPath, FS_OPEN_READ, 0); - - fs_free_path_utf8(fsPath); - } else { - res = R_APP_OUT_OF_MEMORY; - } - - return res; -} - -static Result action_import_twl_save_close_src(void* data, u32 index, bool succeeded, u32 handle) { - return FSFILE_Close(handle); -} - -static Result action_import_twl_save_get_src_size(void* data, u32 handle, u64* size) { - return FSFILE_GetSize(handle, size); -} - -static Result action_import_twl_save_read_src(void* data, u32 handle, u32* bytesRead, void* buffer, u64 offset, u32 size) { - return FSFILE_Read(handle, bytesRead, offset, buffer, size); -} - -static Result action_import_twl_save_open_dst(void* data, u32 index, void* initialReadBlock, u64 size, u32* handle) { - return spi_init_card(); -} - -static Result action_import_twl_save_close_dst(void* data, u32 index, bool succeeded, u32 handle) { - return spi_deinit_card(); -} - -static Result action_import_twl_save_write_dst(void* data, u32 handle, u32* bytesWritten, void* buffer, u64 offset, u32 size) { - return spi_write_save(bytesWritten, buffer, (u32) offset, size); -} - -static Result action_import_twl_save_suspend(void* data, u32 index) { - return 0; -} - -static Result action_import_twl_save_restore(void* data, u32 index) { - return 0; -} - -static bool action_import_twl_save_error(void* data, u32 index, Result res, ui_view** errorView) { - *errorView = error_display_res(((import_twl_save_data*) data)->title, task_draw_title_info, res, "无法导入数据."); - return true; -} - -static void action_import_twl_save_update(ui_view* view, void* data, float* progress, char* text) { - import_twl_save_data* importData = (import_twl_save_data*) data; - - if(importData->importInfo.finished) { - ui_pop(); - info_destroy(view); - - if(R_SUCCEEDED(importData->importInfo.result)) { - prompt_display_notify("成功", "已导入.", COLOR_TEXT, importData->title, task_draw_title_info, NULL); - } - - free(data); - - return; - } - - if(hidKeysDown() & KEY_B) { - svcSignalEvent(importData->importInfo.cancelEvent); - } - - *progress = importData->importInfo.currTotal != 0 ? (float) ((double) importData->importInfo.currProcessed / (double) importData->importInfo.currTotal) : 0; - snprintf(text, PROGRESS_TEXT_MAX, "%.2f %s / %.2f %s\n%.2f %s/s, ETA %s", - ui_get_display_size(importData->importInfo.currProcessed), - ui_get_display_size_units(importData->importInfo.currProcessed), - ui_get_display_size(importData->importInfo.currTotal), - ui_get_display_size_units(importData->importInfo.currTotal), - ui_get_display_size(importData->importInfo.bytesPerSecond), - ui_get_display_size_units(importData->importInfo.bytesPerSecond), - ui_get_display_eta(importData->importInfo.estimatedRemainingSeconds)); -} - -static void action_import_twl_save_onresponse(ui_view* view, void* data, u32 response) { - if(response == PROMPT_YES) { - import_twl_save_data* importData = (import_twl_save_data*) data; - - Result res = task_data_op(&importData->importInfo); - if(R_SUCCEEDED(res)) { - info_display("正在导入", "按 B 取消.", true, data, action_import_twl_save_update, action_import_twl_save_draw_top); - } else { - error_display_res(importData->title, task_draw_title_info, res, "无法启动导入数据."); - free(data); - } - } else { - free(data); - } -} - -void action_import_twl_save(linked_list* items, list_item* selected) { - import_twl_save_data* data = (import_twl_save_data*) calloc(1, sizeof(import_twl_save_data)); - if(data == NULL) { - error_display(NULL, NULL, "无法分配导入 TWL 数据的数据."); - - return; - } - - data->title = (title_info*) selected->data; - - data->importInfo.data = data; - - data->importInfo.op = DATAOP_COPY; - - data->importInfo.bufferSize = 16 * 1024; - data->importInfo.copyEmpty = true; - - data->importInfo.total = 1; - - data->importInfo.isSrcDirectory = action_import_twl_save_is_src_directory; - data->importInfo.makeDstDirectory = action_import_twl_save_make_dst_directory; - - data->importInfo.openSrc = action_import_twl_save_open_src; - data->importInfo.closeSrc = action_import_twl_save_close_src; - data->importInfo.getSrcSize = action_import_twl_save_get_src_size; - data->importInfo.readSrc = action_import_twl_save_read_src; - - data->importInfo.openDst = action_import_twl_save_open_dst; - data->importInfo.closeDst = action_import_twl_save_close_dst; - data->importInfo.writeDst = action_import_twl_save_write_dst; - - data->importInfo.suspend = action_import_twl_save_suspend; - data->importInfo.restore = action_import_twl_save_restore; - - data->importInfo.error = action_import_twl_save_error; - - data->importInfo.finished = true; - - prompt_display_yes_no("确认", "导入数据至所选应用?", COLOR_TEXT, data, action_import_twl_save_draw_top, action_import_twl_save_onresponse); -} diff --git a/source/fbi/action/installcias.c b/source/fbi/action/installcias.c deleted file mode 100644 index 8b0fe9a..0000000 --- a/source/fbi/action/installcias.c +++ /dev/null @@ -1,396 +0,0 @@ -#include -#include -#include - -#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; - - bool delete; - - volatile bool n3dsContinue; - - data_op_data installInfo; -} install_cias_data; - -static void action_install_cias_n3ds_onresponse(ui_view* view, void* data, u32 response) { - ((install_cias_data*) data)->n3dsContinue = response == PROMPT_YES; -} - -static void action_install_cias_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - install_cias_data* installData = (install_cias_data*) data; - - u32 curr = installData->installInfo.processed; - if(curr < installData->installInfo.total) { - task_draw_file_info(view, ((list_item*) linked_list_get(&installData->contents, curr))->data, x1, y1, x2, y2); - } else { - task_draw_file_info(view, installData->target, x1, y1, x2, y2); - } -} - -static Result action_install_cias_is_src_directory(void* data, u32 index, bool* isDirectory) { - *isDirectory = false; - return 0; -} - -static Result action_install_cias_make_dst_directory(void* data, u32 index) { - return 0; -} - -static Result action_install_cias_open_src(void* data, u32 index, u32* handle) { - install_cias_data* installData = (install_cias_data*) data; - - file_info* info = (file_info*) ((list_item*) linked_list_get(&installData->contents, index))->data; - - Result res = 0; - - FS_Path* fsPath = fs_make_path_utf8(info->path); - if(fsPath != NULL) { - res = FSUSER_OpenFile(handle, info->archive, *fsPath, FS_OPEN_READ, 0); - - fs_free_path_utf8(fsPath); - } else { - res = R_APP_OUT_OF_MEMORY; - } - - return res; -} - -static Result action_install_cias_close_src(void* data, u32 index, bool succeeded, u32 handle) { - install_cias_data* installData = (install_cias_data*) data; - - file_info* info = (file_info*) ((list_item*) linked_list_get(&installData->contents, index))->data; - - Result res = 0; - - if(R_SUCCEEDED(res = FSFILE_Close(handle)) && installData->delete && succeeded) { - FS_Path* fsPath = fs_make_path_utf8(info->path); - if(fsPath != NULL) { - if(R_SUCCEEDED(FSUSER_DeleteFile(info->archive, *fsPath))) { - linked_list_iter iter; - linked_list_iterate(installData->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); - } - } - } - - fs_free_path_utf8(fsPath); - } else { - res = R_APP_OUT_OF_MEMORY; - } - } - - return res; -} - -static Result action_install_cias_get_src_size(void* data, u32 handle, u64* size) { - return FSFILE_GetSize(handle, size); -} - -static Result action_install_cias_read_src(void* data, u32 handle, u32* bytesRead, void* buffer, u64 offset, u32 size) { - return FSFILE_Read(handle, bytesRead, offset, buffer, size); -} - -static Result action_install_cias_open_dst(void* data, u32 index, void* initialReadBlock, u64 size, u32* handle) { - install_cias_data* installData = (install_cias_data*) data; - - installData->n3dsContinue = false; - - file_info* info = (file_info*) ((list_item*) linked_list_get(&installData->contents, index))->data; - - FS_MediaType dest = fs_get_title_destination(info->ciaInfo.titleId); - - bool n3ds = false; - if(R_SUCCEEDED(APT_CheckNew3DS(&n3ds)) && !n3ds && ((info->ciaInfo.titleId >> 28) & 0xF) == 2) { - ui_view* view = prompt_display_yes_no("确认", "该应用只适用于 New 3DS.\n继续?", COLOR_TEXT, data, action_install_cias_draw_top, action_install_cias_n3ds_onresponse); - if(view != NULL) { - svcWaitSynchronization(view->active, U64_MAX); - } - - if(!installData->n3dsContinue) { - return R_APP_SKIPPED; - } - } - - // Deleting FBI before it reinstalls itself causes issues. - u64 currTitleId = 0; - FS_MediaType currMediaType = MEDIATYPE_NAND; - - if(envIsHomebrew() || R_FAILED(APT_GetAppletInfo((NS_APPID) envGetAptAppId(), &currTitleId, (u8*) &currMediaType, NULL, NULL, NULL)) || info->ciaInfo.titleId != currTitleId || dest != currMediaType) { - AM_DeleteTitle(dest, info->ciaInfo.titleId); - AM_DeleteTicket(info->ciaInfo.titleId); - - if(dest == MEDIATYPE_SD) { - AM_QueryAvailableExternalTitleDatabase(NULL); - } - } - - return AM_StartCiaInstall(dest, handle); -} - -static Result action_install_cias_close_dst(void* data, u32 index, bool succeeded, u32 handle) { - if(succeeded) { - install_cias_data* installData = (install_cias_data*) data; - - file_info* info = (file_info*) ((list_item*) linked_list_get(&installData->contents, index))->data; - - Result res = 0; - if(R_SUCCEEDED(res = AM_FinishCiaInstall(handle))) { - http_download_seed(info->ciaInfo.titleId); - - if((info->ciaInfo.titleId & 0xFFFFFFF) == 0x0000002) { - res = AM_InstallFirm(info->ciaInfo.titleId); - } - } - - return res; - } else { - return AM_CancelCIAInstall(handle); - } -} - -static Result action_install_cias_write_dst(void* data, u32 handle, u32* bytesWritten, void* buffer, u64 offset, u32 size) { - return FSFILE_Write(handle, bytesWritten, offset, buffer, size, 0); -} - -static Result action_install_cias_suspend(void* data, u32 index) { - return 0; -} - -static Result action_install_cias_restore(void* data, u32 index) { - return 0; -} - -bool action_install_cias_error(void* data, u32 index, Result res, ui_view** errorView) { - *errorView = error_display_res(data, action_install_cias_draw_top, res, "无法安装安装包."); - return true; -} - -static void action_install_cias_free_data(install_cias_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_install_cias_update(ui_view* view, void* data, float* progress, char* text) { - install_cias_data* installData = (install_cias_data*) data; - - if(installData->installInfo.finished) { - if(installData->delete) { - FSUSER_ControlArchive(installData->target->archive, ARCHIVE_ACTION_COMMIT_SAVE_DATA, NULL, 0, NULL, 0); - } - - ui_pop(); - info_destroy(view); - - if(R_SUCCEEDED(installData->installInfo.result)) { - prompt_display_notify("成功", "已安装.", COLOR_TEXT, NULL, NULL, NULL); - } - - action_install_cias_free_data(installData); - - return; - } - - if((hidKeysDown() & KEY_B) && !installData->installInfo.finished) { - svcSignalEvent(installData->installInfo.cancelEvent); - } - - *progress = installData->installInfo.currTotal != 0 ? (float) ((double) installData->installInfo.currProcessed / (double) installData->installInfo.currTotal) : 0; - snprintf(text, PROGRESS_TEXT_MAX, "%lu / %lu\n%.2f %s / %.2f %s\n%.2f %s/s, ETA %s", installData->installInfo.processed, installData->installInfo.total, - ui_get_display_size(installData->installInfo.currProcessed), - ui_get_display_size_units(installData->installInfo.currProcessed), - ui_get_display_size(installData->installInfo.currTotal), - ui_get_display_size_units(installData->installInfo.currTotal), - ui_get_display_size(installData->installInfo.bytesPerSecond), - ui_get_display_size_units(installData->installInfo.bytesPerSecond), - ui_get_display_eta(installData->installInfo.estimatedRemainingSeconds)); -} - -static void action_install_cias_onresponse(ui_view* view, void* data, u32 response) { - install_cias_data* installData = (install_cias_data*) data; - - if(response == PROMPT_YES) { - Result res = task_data_op(&installData->installInfo); - if(R_SUCCEEDED(res)) { - info_display("正在安装", "按 B 取消.", true, data, action_install_cias_update, action_install_cias_draw_top); - } else { - error_display_res(NULL, NULL, res, "无法启动安装包安装."); - - action_install_cias_free_data(installData); - } - } else { - action_install_cias_free_data(installData); - } -} - -typedef struct { - install_cias_data* installData; - - const char* message; - - fs_filter_data filterData; - populate_files_data popData; -} install_cias_loading_data; - -static void action_install_cias_loading_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - action_install_cias_draw_top(view, ((install_cias_loading_data*) data)->installData, x1, y1, x2, y2); -} - -static void action_install_cias_loading_update(ui_view* view, void* data, float* progress, char* text) { - install_cias_loading_data* loadingData = (install_cias_loading_data*) data; - - if(loadingData->popData.finished) { - ui_pop(); - info_destroy(view); - - if(R_SUCCEEDED(loadingData->popData.result)) { - loadingData->installData->installInfo.total = linked_list_size(&loadingData->installData->contents); - loadingData->installData->installInfo.processed = loadingData->installData->installInfo.total; - - prompt_display_yes_no("确认", loadingData->message, COLOR_TEXT, loadingData->installData, action_install_cias_draw_top, action_install_cias_onresponse); - } else { - error_display_res(NULL, NULL, loadingData->popData.result, "无法填充安装包列表."); - - action_install_cias_free_data(loadingData->installData); - } - - free(loadingData); - return; - } - - if((hidKeysDown() & KEY_B) && !loadingData->popData.finished) { - svcSignalEvent(loadingData->popData.cancelEvent); - } - - snprintf(text, PROGRESS_TEXT_MAX, "正在获取安装包列表..."); -} - -static void action_install_cias_internal(linked_list* items, list_item* selected, bool (*filter)(void* data, const char* name, u32 attributes), void* filterData, const char* message, bool delete) { - install_cias_data* data = (install_cias_data*) calloc(1, sizeof(install_cias_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, true); - if(R_FAILED(targetCreateRes)) { - error_display_res(NULL, NULL, targetCreateRes, "无法创建目标文件."); - - action_install_cias_free_data(data); - return; - } - - data->target = (file_info*) data->targetItem->data; - - data->delete = delete; - - data->n3dsContinue = false; - - data->installInfo.data = data; - - data->installInfo.op = DATAOP_COPY; - - data->installInfo.bufferSize = 256 * 1024; - data->installInfo.copyEmpty = false; - - data->installInfo.isSrcDirectory = action_install_cias_is_src_directory; - data->installInfo.makeDstDirectory = action_install_cias_make_dst_directory; - - data->installInfo.openSrc = action_install_cias_open_src; - data->installInfo.closeSrc = action_install_cias_close_src; - data->installInfo.getSrcSize = action_install_cias_get_src_size; - data->installInfo.readSrc = action_install_cias_read_src; - - data->installInfo.openDst = action_install_cias_open_dst; - data->installInfo.closeDst = action_install_cias_close_dst; - data->installInfo.writeDst = action_install_cias_write_dst; - - data->installInfo.suspend = action_install_cias_suspend; - data->installInfo.restore = action_install_cias_restore; - - data->installInfo.error = action_install_cias_error; - - data->installInfo.finished = true; - - linked_list_init(&data->contents); - - install_cias_loading_data* loadingData = (install_cias_loading_data*) calloc(1, sizeof(install_cias_loading_data)); - if(loadingData == NULL) { - error_display(NULL, NULL, "无法分配加载的数据."); - - action_install_cias_free_data(data); - return; - } - - loadingData->installData = data; - loadingData->message = message; - - loadingData->filterData.parentFilter = filter; - loadingData->filterData.parentFilterData = filterData; - - 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 = false; - loadingData->popData.includeBase = !(data->target->attributes & FS_ATTRIBUTE_DIRECTORY); - loadingData->popData.meta = true; - loadingData->popData.filter = fs_filter_cias; - loadingData->popData.filterData = &loadingData->filterData; - - Result listRes = task_populate_files(&loadingData->popData); - if(R_FAILED(listRes)) { - error_display_res(NULL, NULL, listRes, "无法启动安装包列表填充."); - - free(loadingData); - action_install_cias_free_data(data); - return; - } - - info_display("正在加载", "按 B 取消.", false, loadingData, action_install_cias_loading_update, action_install_cias_loading_draw_top); -} - -void action_install_cia(linked_list* items, list_item* selected) { - action_install_cias_internal(items, selected, NULL, NULL, "安装所选安装包?", false); -} - -void action_install_cia_delete(linked_list* items, list_item* selected) { - action_install_cias_internal(items, selected, NULL, NULL, "安装并删除所选安装包?", true); -} - -void action_install_cias(linked_list* items, list_item* selected, bool (*filter)(void* data, const char* name, u32 attributes), void* filterData) { - action_install_cias_internal(items, selected, filter, filterData, "安装当前文件夹的所有安装包?", false); -} - -void action_install_cias_delete(linked_list* items, list_item* selected, bool (*filter)(void* data, const char* name, u32 attributes), void* filterData) { - action_install_cias_internal(items, selected, filter, filterData, "安装并删除当前文件夹的所有安装包?", true); -} diff --git a/source/fbi/action/installtickets.c b/source/fbi/action/installtickets.c deleted file mode 100644 index 444c815..0000000 --- a/source/fbi/action/installtickets.c +++ /dev/null @@ -1,343 +0,0 @@ -#include -#include -#include - -#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; - - bool delete; - - data_op_data installInfo; -} install_tickets_data; - -static void action_install_tickets_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - install_tickets_data* installData = (install_tickets_data*) data; - - u32 curr = installData->installInfo.processed; - if(curr < installData->installInfo.total) { - task_draw_file_info(view, ((list_item*) linked_list_get(&installData->contents, curr))->data, x1, y1, x2, y2); - } else { - task_draw_file_info(view, installData->target, x1, y1, x2, y2); - } -} - -static Result action_install_tickets_is_src_directory(void* data, u32 index, bool* isDirectory) { - *isDirectory = false; - return 0; -} - -static Result action_install_tickets_make_dst_directory(void* data, u32 index) { - return 0; -} - -static Result action_install_tickets_open_src(void* data, u32 index, u32* handle) { - install_tickets_data* installData = (install_tickets_data*) data; - - file_info* info = (file_info*) ((list_item*) linked_list_get(&installData->contents, index))->data; - - Result res = 0; - - FS_Path* fsPath = fs_make_path_utf8(info->path); - if(fsPath != NULL) { - res = FSUSER_OpenFile(handle, info->archive, *fsPath, FS_OPEN_READ, 0); - - fs_free_path_utf8(fsPath); - } else { - res = R_APP_OUT_OF_MEMORY; - } - - return res; -} - -static Result action_install_tickets_close_src(void* data, u32 index, bool succeeded, u32 handle) { - install_tickets_data* installData = (install_tickets_data*) data; - - file_info* info = (file_info*) ((list_item*) linked_list_get(&installData->contents, index))->data; - - Result res = 0; - - if(R_SUCCEEDED(res = FSFILE_Close(handle)) && installData->delete && succeeded) { - FS_Path* fsPath = fs_make_path_utf8(info->path); - if(fsPath != NULL) { - if(R_SUCCEEDED(FSUSER_DeleteFile(info->archive, *fsPath))) { - linked_list_iter iter; - linked_list_iterate(installData->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); - } - } - } - - fs_free_path_utf8(fsPath); - } else { - res = R_APP_OUT_OF_MEMORY; - } - } - - return res; -} - -static Result action_install_tickets_get_src_size(void* data, u32 handle, u64* size) { - return FSFILE_GetSize(handle, size); -} - -static Result action_install_tickets_read_src(void* data, u32 handle, u32* bytesRead, void* buffer, u64 offset, u32 size) { - return FSFILE_Read(handle, bytesRead, offset, buffer, size); -} - -static Result action_install_tickets_open_dst(void* data, u32 index, void* initialReadBlock, u64 size, u32* handle) { - AM_DeleteTicket(((file_info*) ((list_item*) linked_list_get(&((install_tickets_data*) data)->contents, index))->data)->ticketInfo.titleId); - return AM_InstallTicketBegin(handle); -} - -static Result action_install_tickets_close_dst(void* data, u32 index, bool succeeded, u32 handle) { - if(succeeded) { - return AM_InstallTicketFinish(handle); - } else { - return AM_InstallTicketAbort(handle); - } -} - -static Result action_install_tickets_write_dst(void* data, u32 handle, u32* bytesWritten, void* buffer, u64 offset, u32 size) { - return FSFILE_Write(handle, bytesWritten, offset, buffer, size, 0); -} - -static Result action_install_tickets_suspend(void* data, u32 index) { - return 0; -} - -static Result action_install_tickets_restore(void* data, u32 index) { - return 0; -} - -static bool action_install_tickets_error(void* data, u32 index, Result res, ui_view** errorView) { - *errorView = error_display_res(data, action_install_tickets_draw_top, res, "无法安装应用引导表."); - return true; -} - -static void action_install_tickets_free_data(install_tickets_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_install_tickets_update(ui_view* view, void* data, float* progress, char* text) { - install_tickets_data* installData = (install_tickets_data*) data; - - if(installData->installInfo.finished) { - if(installData->delete) { - FSUSER_ControlArchive(installData->target->archive, ARCHIVE_ACTION_COMMIT_SAVE_DATA, NULL, 0, NULL, 0); - } - - ui_pop(); - info_destroy(view); - - if(R_SUCCEEDED(installData->installInfo.result)) { - prompt_display_notify("成功", "已安装.", COLOR_TEXT, NULL, NULL, NULL); - } - - action_install_tickets_free_data(installData); - - return; - } - - if((hidKeysDown() & KEY_B) && !installData->installInfo.finished) { - svcSignalEvent(installData->installInfo.cancelEvent); - } - - *progress = installData->installInfo.currTotal != 0 ? (float) ((double) installData->installInfo.currProcessed / (double) installData->installInfo.currTotal) : 0; - snprintf(text, PROGRESS_TEXT_MAX, "%lu / %lu\n%.2f %s / %.2f %s\n%.2f %s/s, ETA %s", installData->installInfo.processed, installData->installInfo.total, - ui_get_display_size(installData->installInfo.currProcessed), - ui_get_display_size_units(installData->installInfo.currProcessed), - ui_get_display_size(installData->installInfo.currTotal), - ui_get_display_size_units(installData->installInfo.currTotal), - ui_get_display_size(installData->installInfo.bytesPerSecond), - ui_get_display_size_units(installData->installInfo.bytesPerSecond), - ui_get_display_eta(installData->installInfo.estimatedRemainingSeconds)); -} - -static void action_install_tickets_onresponse(ui_view* view, void* data, u32 response) { - install_tickets_data* installData = (install_tickets_data*) data; - - if(response == PROMPT_YES) { - Result res = task_data_op(&installData->installInfo); - if(R_SUCCEEDED(res)) { - info_display("正在安装", "按 B 取消.", true, data, action_install_tickets_update, action_install_tickets_draw_top); - } else { - error_display_res(NULL, NULL, res, "无法启动应用引导表安装."); - - action_install_tickets_free_data(installData); - } - } else { - action_install_tickets_free_data(installData); - } -} - -typedef struct { - install_tickets_data* installData; - - const char* message; - - fs_filter_data filterData; - populate_files_data popData; -} install_tickets_loading_data; - -static void action_install_tickets_loading_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - action_install_tickets_draw_top(view, ((install_tickets_loading_data*) data)->installData, x1, y1, x2, y2); -} - -static void action_install_tickets_loading_update(ui_view* view, void* data, float* progress, char* text) { - install_tickets_loading_data* loadingData = (install_tickets_loading_data*) data; - - if(loadingData->popData.finished) { - ui_pop(); - info_destroy(view); - - if(R_SUCCEEDED(loadingData->popData.result)) { - loadingData->installData->installInfo.total = linked_list_size(&loadingData->installData->contents); - loadingData->installData->installInfo.processed = loadingData->installData->installInfo.total; - - prompt_display_yes_no("Confirmation", loadingData->message, COLOR_TEXT, loadingData->installData, action_install_tickets_draw_top, action_install_tickets_onresponse); - } else { - error_display_res(NULL, NULL, loadingData->popData.result, "无法填充应用引导表列表."); - - action_install_tickets_free_data(loadingData->installData); - } - - free(loadingData); - return; - } - - if((hidKeysDown() & KEY_B) && !loadingData->popData.finished) { - svcSignalEvent(loadingData->popData.cancelEvent); - } - - snprintf(text, PROGRESS_TEXT_MAX, "正在获取应用引导表列表..."); -} - -static void action_install_tickets_internal(linked_list* items, list_item* selected, bool (*filter)(void* data, const char* name, u32 attributes), void* filterData, const char* message, bool delete) { - install_tickets_data* data = (install_tickets_data*) calloc(1, sizeof(install_tickets_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, true); - if(R_FAILED(targetCreateRes)) { - error_display_res(NULL, NULL, targetCreateRes, "无法创建目标文件."); - - action_install_tickets_free_data(data); - return; - } - - data->target = (file_info*) data->targetItem->data; - - data->delete = delete; - - data->installInfo.data = data; - - data->installInfo.op = DATAOP_COPY; - - data->installInfo.bufferSize = 256 * 1024; - data->installInfo.copyEmpty = false; - - data->installInfo.isSrcDirectory = action_install_tickets_is_src_directory; - data->installInfo.makeDstDirectory = action_install_tickets_make_dst_directory; - - data->installInfo.openSrc = action_install_tickets_open_src; - data->installInfo.closeSrc = action_install_tickets_close_src; - data->installInfo.getSrcSize = action_install_tickets_get_src_size; - data->installInfo.readSrc = action_install_tickets_read_src; - - data->installInfo.openDst = action_install_tickets_open_dst; - data->installInfo.closeDst = action_install_tickets_close_dst; - data->installInfo.writeDst = action_install_tickets_write_dst; - - data->installInfo.suspend = action_install_tickets_suspend; - data->installInfo.restore = action_install_tickets_restore; - - data->installInfo.error = action_install_tickets_error; - - data->installInfo.finished = true; - - linked_list_init(&data->contents); - - install_tickets_loading_data* loadingData = (install_tickets_loading_data*) calloc(1, sizeof(install_tickets_loading_data)); - if(loadingData == NULL) { - error_display(NULL, NULL, "无法分配加载的数据."); - - action_install_tickets_free_data(data); - return; - } - - loadingData->installData = data; - loadingData->message = message; - - loadingData->filterData.parentFilter = filter; - loadingData->filterData.parentFilterData = filterData; - - 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 = false; - loadingData->popData.includeBase = !(data->target->attributes & FS_ATTRIBUTE_DIRECTORY); - loadingData->popData.meta = true; - loadingData->popData.filter = fs_filter_tickets; - loadingData->popData.filterData = &loadingData->filterData; - - Result listRes = task_populate_files(&loadingData->popData); - if(R_FAILED(listRes)) { - error_display_res(NULL, NULL, listRes, "无法启动应用引导表列表填充."); - - free(loadingData); - action_install_tickets_free_data(data); - return; - } - - info_display("Loading", "Press B to cancel.", false, loadingData, action_install_tickets_loading_update, action_install_tickets_loading_draw_top); -} - -void action_install_ticket(linked_list* items, list_item* selected) { - action_install_tickets_internal(items, selected, NULL, NULL, "安装所选应用引导表?", false); -} - -void action_install_ticket_delete(linked_list* items, list_item* selected) { - action_install_tickets_internal(items, selected, NULL, NULL, "安装并删除所选应用引导表?", true); -} - -void action_install_tickets(linked_list* items, list_item* selected, bool (*filter)(void* data, const char* name, u32 attributes), void* filterData) { - action_install_tickets_internal(items, selected, filter, filterData, "安装当前文件夹的所有应用引导表?", false); -} - -void action_install_tickets_delete(linked_list* items, list_item* selected, bool (*filter)(void* data, const char* name, u32 attributes), void* filterData) { - action_install_tickets_internal(items, selected, filter, filterData, "安装并删除当前文件夹的所有应用引导表?", true); -} diff --git a/source/fbi/action/installurl.c b/source/fbi/action/installurl.c deleted file mode 100644 index bd4a32d..0000000 --- a/source/fbi/action/installurl.c +++ /dev/null @@ -1,403 +0,0 @@ -#include -#include -#include - -#include <3ds.h> - -#include "action.h" -#include "../resources.h" -#include "../task/uitask.h" -#include "../../core/core.h" - -typedef enum content_type_e { - CONTENT_CIA, - CONTENT_TICKET, - CONTENT_3DSX_SMDH -} content_type; - -typedef struct { - char urls[INSTALL_URLS_MAX][DOWNLOAD_URL_MAX]; - - char paths[INSTALL_URLS_MAX][FILE_PATH_MAX]; - - 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); - - content_type contentType; - u64 currTitleId; - volatile bool n3dsContinue; - ticket_info ticketInfo; - char currPath[FILE_PATH_MAX]; - - data_op_data installInfo; -} install_url_data; - -static void action_install_url_free_data(install_url_data* data) { - if(data->finishedAll != NULL) { - data->finishedAll(data->userData); - } - - free(data); -} - -static void action_install_url_n3ds_onresponse(ui_view* view, void* data, u32 response) { - ((install_url_data*) data)->n3dsContinue = response == PROMPT_YES; -} - -static void action_install_url_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - install_url_data* installData = (install_url_data*) data; - - if(installData->drawTop != NULL) { - installData->drawTop(view, installData->userData, x1, y1, x2, y2, installData->installInfo.processed); - } else if(installData->installInfo.processed == installData->installInfo.total) { - float urlY = y1 + 5; - u32 index = 0; - while(urlY < y2 && index < installData->installInfo.total) { - float urlWidth = 0; - float urlHeight = 0; - screen_get_string_size_wrap(&urlWidth, &urlHeight, installData->urls[index], 0.5f, 0.5f, x2 - x1 - 10); - - float urlX = x1 + (x2 - x1 - urlWidth) / 2; - screen_draw_string_wrap(installData->urls[index], urlX, urlY, 0.5f, 0.5f, COLOR_TEXT, true, urlX + urlWidth + 1); - - urlY += urlHeight; - index++; - } - } else { - float urlWidth = 0; - float urlHeight = 0; - screen_get_string_size_wrap(&urlWidth, &urlHeight, installData->urls[installData->installInfo.processed], 0.5f, 0.5f, x2 - x1 - 10); - - float urlX = x1 + (x2 - x1 - urlWidth) / 2; - float urlY = y1 + (y2 - y1 - urlHeight) / 2; - screen_draw_string_wrap(installData->urls[installData->installInfo.processed], urlX, urlY, 0.5f, 0.5f, COLOR_TEXT, true, urlX + urlWidth + 1); - } -} - -static Result action_install_url_get_src_url(void* data, u32 index, char* url, size_t maxSize) { - install_url_data* installData = (install_url_data*) data; - - strncpy(url, installData->urls[index], maxSize); - return 0; -} - -static Result action_install_url_open_dst(void* data, u32 index, void* initialReadBlock, u64 size, u32* handle) { - install_url_data* installData = (install_url_data*) data; - - Result res = 0; - - installData->contentType = CONTENT_CIA; - installData->currTitleId = 0; - installData->n3dsContinue = false; - memset(&installData->ticketInfo, 0, sizeof(installData->ticketInfo)); - memset(&installData->currPath, 0, sizeof(installData->currPath)); - - if(*(u16*) initialReadBlock == 0x2020) { - installData->contentType = CONTENT_CIA; - - u64 titleId = 0; - if(R_SUCCEEDED(res = cia_get_title_id(&titleId, (u8*) initialReadBlock, installData->installInfo.bufferSize))) { - FS_MediaType dest = fs_get_title_destination(titleId); - - bool n3ds = false; - if(R_SUCCEEDED(APT_CheckNew3DS(&n3ds)) && !n3ds && ((titleId >> 28) & 0xF) == 2) { - ui_view* view = prompt_display_yes_no("确认", "该应用只适用于 New 3DS.\n继续?", COLOR_TEXT, data, action_install_url_draw_top, action_install_url_n3ds_onresponse); - if(view != NULL) { - svcWaitSynchronization(view->active, U64_MAX); - } - - if(!installData->n3dsContinue) { - return R_APP_SKIPPED; - } - } - - // Deleting FBI before it reinstalls itself causes issues. - u64 currTitleId = 0; - FS_MediaType currMediaType = MEDIATYPE_NAND; - - if(envIsHomebrew() || R_FAILED(APT_GetAppletInfo((NS_APPID) envGetAptAppId(), &currTitleId, (u8*) &currMediaType, NULL, NULL, NULL)) || titleId != currTitleId || dest != currMediaType) { - AM_DeleteTitle(dest, titleId); - AM_DeleteTicket(titleId); - - if(dest == MEDIATYPE_SD) { - AM_QueryAvailableExternalTitleDatabase(NULL); - } - } - - if(R_SUCCEEDED(res = AM_StartCiaInstall(dest, handle))) { - installData->currTitleId = titleId; - } - } - } else if(*(u16*) initialReadBlock == 0x0100) { - if(R_SUCCEEDED(res = ticket_get_title_id(&installData->ticketInfo.titleId, (u8*) initialReadBlock, installData->installInfo.bufferSize))) { - installData->contentType = CONTENT_TICKET; - - installData->ticketInfo.inUse = false; - installData->ticketInfo.loaded = true; - - AM_DeleteTicket(installData->ticketInfo.titleId); - res = AM_InstallTicketBegin(handle); - } - } else if(*(u32*) initialReadBlock == 0x58534433 /* 3DSX */ || *(u32*) initialReadBlock == 0x48444D53 /* SMDH */) { - installData->contentType = CONTENT_3DSX_SMDH; - - FS_Archive sdmcArchive = 0; - if(R_SUCCEEDED(res = FSUSER_OpenArchive(&sdmcArchive, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")))) { - char dir[FILE_PATH_MAX]; - if(strlen(installData->paths[index]) > 0) { - string_get_parent_path(dir, installData->paths[index], FILE_PATH_MAX); - string_copy(installData->currPath, installData->paths[index], FILE_PATH_MAX); - } else { - char filename[FILE_NAME_MAX]; - string_get_path_file(filename, installData->urls[index], FILE_NAME_MAX); - - char name[FILE_NAME_MAX]; - string_get_file_name(name, filename, FILE_NAME_MAX); - - snprintf(dir, FILE_PATH_MAX, "/3ds/%s/", name); - snprintf(installData->currPath, FILE_PATH_MAX, "/3ds/%s/%s.%s", name, name, *(u32*) initialReadBlock == 0x58534433 ? "3dsx" : "smdh"); - } - - if(R_SUCCEEDED(res = fs_ensure_dir(sdmcArchive, "/3ds/")) && R_SUCCEEDED(res = fs_ensure_dir(sdmcArchive, dir))) { - FS_Path* path = fs_make_path_utf8(installData->currPath); - if(path != NULL) { - res = FSUSER_OpenFileDirectly(handle, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), *path, FS_OPEN_WRITE | FS_OPEN_CREATE, 0); - - fs_free_path_utf8(path); - } else { - res = R_APP_OUT_OF_MEMORY; - } - } - - FSUSER_CloseArchive(sdmcArchive); - } - } else { - res = R_APP_BAD_DATA; - } - - return res; -} - -static Result action_install_url_close_dst(void* data, u32 index, bool succeeded, u32 handle) { - install_url_data* installData = (install_url_data*) data; - - Result res = 0; - - if(succeeded) { - if(installData->contentType == CONTENT_CIA) { - if(R_SUCCEEDED(res = AM_FinishCiaInstall(handle))) { - http_download_seed(installData->currTitleId); - - if(installData->currTitleId == 0x0004013800000002 || installData->currTitleId == 0x0004013820000002) { - res = AM_InstallFirm(installData->currTitleId); - } - } - } else if(installData->contentType == CONTENT_TICKET) { - res = AM_InstallTicketFinish(handle); - } else if(installData->contentType == CONTENT_3DSX_SMDH) { - res = FSFILE_Close(handle); - } - } else { - if(installData->contentType == CONTENT_CIA) { - res = AM_CancelCIAInstall(handle); - } else if(installData->contentType == CONTENT_TICKET) { - res = AM_InstallTicketAbort(handle); - } else if(installData->contentType == CONTENT_3DSX_SMDH) { - res = FSFILE_Close(handle); - - FS_Archive sdmcArchive = 0; - if(R_SUCCEEDED(FSUSER_OpenArchive(&sdmcArchive, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")))) { - FS_Path* path = fs_make_path_utf8(installData->currPath); - if(path != NULL) { - FSUSER_DeleteFile(sdmcArchive, *path); - - fs_free_path_utf8(path); - } - - FSUSER_CloseArchive(sdmcArchive); - } - } - } - - if(R_SUCCEEDED(res) && installData->finishedURL != NULL) { - installData->finishedURL(installData->userData, index); - } - - return res; -} - -static Result action_install_url_write_dst(void* data, u32 handle, u32* bytesWritten, void* buffer, u64 offset, u32 size) { - return FSFILE_Write(handle, bytesWritten, offset, buffer, size, 0); -} - -static Result action_install_url_suspend(void* data, u32 index) { - return 0; -} - -static Result action_install_url_restore(void* data, u32 index) { - return 0; -} - -static bool action_install_url_error(void* data, u32 index, Result res, ui_view** errorView) { - install_url_data* installData = (install_url_data*) data; - - char* url = installData->urls[index]; - if(strlen(url) > 38) { - *errorView = error_display_res(data, action_install_url_draw_top, res, "无法从链接安装.\n%.35s...", url); - } else { - *errorView = error_display_res(data, action_install_url_draw_top, res, "无法从链接安装.\n%.38s", url); - } - - return true; -} - -static void action_install_url_install_update(ui_view* view, void* data, float* progress, char* text) { - install_url_data* installData = (install_url_data*) data; - - if(installData->installInfo.finished) { - ui_pop(); - info_destroy(view); - - if(R_SUCCEEDED(installData->installInfo.result)) { - prompt_display_notify("成功", "已安装.", COLOR_TEXT, NULL, NULL, NULL); - } - - action_install_url_free_data(installData); - - return; - } - - if(hidKeysDown() & KEY_B) { - svcSignalEvent(installData->installInfo.cancelEvent); - } - - *progress = installData->installInfo.currTotal != 0 ? (float) ((double) installData->installInfo.currProcessed / (double) installData->installInfo.currTotal) : 0; - snprintf(text, PROGRESS_TEXT_MAX, "%lu / %lu\n%.2f %s / %.2f %s\n%.2f %s/s, ETA %s", installData->installInfo.processed, installData->installInfo.total, - ui_get_display_size(installData->installInfo.currProcessed), - ui_get_display_size_units(installData->installInfo.currProcessed), - ui_get_display_size(installData->installInfo.currTotal), - ui_get_display_size_units(installData->installInfo.currTotal), - ui_get_display_size(installData->installInfo.bytesPerSecond), - ui_get_display_size_units(installData->installInfo.bytesPerSecond), - ui_get_display_eta(installData->installInfo.estimatedRemainingSeconds)); -} - -static void action_install_url_confirm_onresponse(ui_view* view, void* data, u32 response) { - install_url_data* installData = (install_url_data*) data; - - if(response == PROMPT_YES) { - Result res = task_data_op(&installData->installInfo); - if(R_SUCCEEDED(res)) { - info_display("正在安装", "按 B 取消.", true, data, action_install_url_install_update, action_install_url_draw_top); - } else { - error_display_res(NULL, NULL, res, "无法启动链接安装."); - - action_install_url_free_data(installData); - } - } else { - action_install_url_free_data(installData); - } -} - -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)) { - install_url_data* data = (install_url_data*) calloc(1, sizeof(install_url_data)); - if(data == NULL) { - error_display(NULL, NULL, "无法分配链接安装的数据."); - - return; - } - - data->installInfo.total = 0; - - size_t payloadLen = strlen(urls); - if(payloadLen > 0) { - const char* currStart = urls; - while(data->installInfo.total < INSTALL_URLS_MAX && currStart - urls < payloadLen) { - const char* currEnd = strchr(currStart, '\n'); - if(currEnd == NULL) { - currEnd = urls + payloadLen; - } - - u32 len = currEnd - currStart; - - if((len < 7 || strncmp(currStart, "http://", 7) != 0) && (len < 8 || strncmp(currStart, "https://", 8) != 0)) { - if(len > DOWNLOAD_URL_MAX - 8) { - len = DOWNLOAD_URL_MAX - 8; - } - - string_copy(data->urls[data->installInfo.total], "http://", 8); - string_copy(&data->urls[data->installInfo.total][7], currStart, len + 1); - } else { - if(len > DOWNLOAD_URL_MAX - 1) { - len = DOWNLOAD_URL_MAX - 1; - } - - string_copy(data->urls[data->installInfo.total], currStart, len + 1); - } - - data->installInfo.total++; - currStart = currEnd + 1; - } - } - - if(paths != NULL) { - size_t pathsLen = strlen(paths); - if(pathsLen > 0) { - const char* currStart = paths; - for(u32 i = 0; i < data->installInfo.total && currStart - paths < pathsLen; i++) { - const char* currEnd = strchr(currStart, '\n'); - if(currEnd == NULL) { - currEnd = paths + pathsLen; - } - - u32 len = currEnd - currStart; - if(len > FILE_PATH_MAX - 1) { - len = FILE_PATH_MAX - 1; - } - - string_copy(data->paths[i], currStart, len + 1); - - currStart = currEnd + 1; - } - } - } - - data->userData = userData; - data->finishedURL = finishedURL; - data->finishedAll = finishedAll; - data->drawTop = drawTop; - - data->contentType = CONTENT_CIA; - data->currTitleId = 0; - data->n3dsContinue = false; - memset(&data->ticketInfo, 0, sizeof(data->ticketInfo)); - memset(&data->currPath, 0, sizeof(data->currPath)); - - data->installInfo.data = data; - - data->installInfo.op = DATAOP_DOWNLOAD; - - data->installInfo.bufferSize = 128 * 1024; - - data->installInfo.processed = data->installInfo.total; - - data->installInfo.getSrcUrl = action_install_url_get_src_url; - - data->installInfo.openDst = action_install_url_open_dst; - data->installInfo.closeDst = action_install_url_close_dst; - data->installInfo.writeDst = action_install_url_write_dst; - - data->installInfo.suspend = action_install_url_suspend; - data->installInfo.restore = action_install_url_restore; - - data->installInfo.error = action_install_url_error; - - data->installInfo.finished = true; - - prompt_display_yes_no("确认", confirmMessage, COLOR_TEXT, data, action_install_url_draw_top, action_install_url_confirm_onresponse); -} diff --git a/source/fbi/action/launchtitle.c b/source/fbi/action/launchtitle.c deleted file mode 100644 index 30d0ccf..0000000 --- a/source/fbi/action/launchtitle.c +++ /dev/null @@ -1,36 +0,0 @@ -#include <3ds.h> - -#include "action.h" -#include "../resources.h" -#include "../task/uitask.h" -#include "../../core/core.h" - -static void action_launch_title_update(ui_view* view, void* data, float* progress, char* text) { - title_info* info = (title_info*) data; - - Result res = 0; - - if(R_SUCCEEDED(res = APT_PrepareToDoApplicationJump(0, info->titleId, info->mediaType))) { - u8 param[0x300]; - u8 hmac[0x20]; - - res = APT_DoApplicationJump(param, sizeof(param), hmac); - } - - if(R_FAILED(res)) { - ui_pop(); - info_destroy(view); - - error_display_res(info, task_draw_title_info, res, "无法启动应用."); - } -} - -static void action_launch_title_onresponse(ui_view* view, void* data, u32 response) { - if(response == PROMPT_YES) { - info_display("正在启动", "", false, data, action_launch_title_update, task_draw_title_info); - } -} - -void action_launch_title(linked_list* items, list_item* selected) { - prompt_display_yes_no("确认", "启动所选应用?", COLOR_TEXT, selected->data, task_draw_title_info, action_launch_title_onresponse); -} diff --git a/source/fbi/action/newfolder.c b/source/fbi/action/newfolder.c deleted file mode 100644 index 2c66209..0000000 --- a/source/fbi/action/newfolder.c +++ /dev/null @@ -1,67 +0,0 @@ -#include -#include - -#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; -} new_folder_data; - -static void action_new_folder_onresponse(ui_view* view, void* data, SwkbdButton button, const char* response) { - new_folder_data* newFolderData = (new_folder_data*) data; - - if(button == SWKBD_BUTTON_CONFIRM) { - Result res = 0; - - file_info* parentDir = (file_info*) newFolderData->selected->data; - - char fileName[FILE_NAME_MAX] = {'\0'}; - string_escape_file_name(fileName, response, sizeof(fileName)); - - char path[FILE_PATH_MAX] = {'\0'}; - snprintf(path, FILE_PATH_MAX, "%s%s", parentDir->path, fileName); - - FS_Path* fsPath = fs_make_path_utf8(path); - if(fsPath != NULL) { - res = FSUSER_CreateDirectory(parentDir->archive, *fsPath, FS_ATTRIBUTE_DIRECTORY); - - fs_free_path_utf8(fsPath); - } else { - res = R_APP_OUT_OF_MEMORY; - } - - if(R_SUCCEEDED(res)) { - list_item* folderItem = NULL; - if(R_SUCCEEDED(task_create_file_item(&folderItem, parentDir->archive, path, FS_ATTRIBUTE_DIRECTORY, true))) { - linked_list_add(newFolderData->items, folderItem); - linked_list_sort(newFolderData->items, NULL, task_compare_files); - } - - prompt_display_notify("成功", "已创建.", COLOR_TEXT, NULL, NULL, NULL); - } else { - error_display_res(NULL, NULL, res, "无法创建文件夹."); - } - } - - free(newFolderData); -} - -void action_new_folder(linked_list* items, list_item* selected) { - new_folder_data* data = (new_folder_data*) calloc(1, sizeof(new_folder_data)); - if(data == NULL) { - error_display(NULL, NULL, "无法分配新建文件夹的数据."); - - return; - } - - data->items = items; - data->selected = selected; - - kbd_display("输入文件夹名称", "", SWKBD_TYPE_NORMAL, 0, SWKBD_NOTEMPTY_NOTBLANK, FILE_NAME_MAX, data, action_new_folder_onresponse); -} diff --git a/source/fbi/action/pastecontents.c b/source/fbi/action/pastecontents.c deleted file mode 100644 index 5eacd11..0000000 --- a/source/fbi/action/pastecontents.c +++ /dev/null @@ -1,404 +0,0 @@ -#include -#include -#include - -#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 pasteInfo; -} paste_contents_data; - -static void action_paste_contents_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - paste_contents_data* pasteData = (paste_contents_data*) data; - - u32 curr = pasteData->pasteInfo.processed; - if(curr < pasteData->pasteInfo.total) { - task_draw_file_info(view, ((list_item*) linked_list_get(&pasteData->contents, curr))->data, x1, y1, x2, y2); - } else { - task_draw_file_info(view, pasteData->target, x1, y1, x2, y2); - } -} - -static void action_paste_contents_get_dst_path(paste_contents_data* data, u32 index, char* dstPath) { - char baseSrcPath[FILE_PATH_MAX]; - if(clipboard_is_contents_only()) { - string_copy(baseSrcPath, clipboard_get_path(), FILE_PATH_MAX); - } else { - string_get_parent_path(baseSrcPath, clipboard_get_path(), FILE_PATH_MAX); - } - - char baseDstPath[FILE_PATH_MAX]; - if(data->target->attributes & FS_ATTRIBUTE_DIRECTORY) { - string_copy(baseDstPath, data->target->path, FILE_PATH_MAX); - } else { - string_get_parent_path(baseDstPath, data->target->path, FILE_PATH_MAX); - } - - snprintf(dstPath, FILE_PATH_MAX, "%s%s", baseDstPath, ((file_info*) ((list_item*) linked_list_get(&data->contents, index))->data)->path + strlen(baseSrcPath)); -} - -static Result action_paste_contents_is_src_directory(void* data, u32 index, bool* isDirectory) { - paste_contents_data* pasteData = (paste_contents_data*) data; - - *isDirectory = (bool) (((file_info*) ((list_item*) linked_list_get(&pasteData->contents, index))->data)->attributes & FS_ATTRIBUTE_DIRECTORY); - return 0; -} - -static Result action_paste_contents_make_dst_directory(void* data, u32 index) { - paste_contents_data* pasteData = (paste_contents_data*) data; - - Result res = 0; - - u32 attributes = ((file_info*) ((list_item*) linked_list_get(&pasteData->contents, index))->data)->attributes; - - char dstPath[FILE_PATH_MAX]; - action_paste_contents_get_dst_path(pasteData, index, dstPath); - - FS_Path* fsPath = fs_make_path_utf8(dstPath); - if(fsPath != NULL) { - Handle dirHandle = 0; - if(R_SUCCEEDED(FSUSER_OpenDirectory(&dirHandle, pasteData->target->archive, *fsPath))) { - FSDIR_Close(dirHandle); - } else if(R_SUCCEEDED(res = FSUSER_CreateDirectory(pasteData->target->archive, *fsPath, attributes))) { - char parentPath[FILE_PATH_MAX]; - string_get_parent_path(parentPath, dstPath, FILE_PATH_MAX); - - char baseDstPath[FILE_PATH_MAX]; - if(pasteData->target->attributes & FS_ATTRIBUTE_DIRECTORY) { - string_copy(baseDstPath, pasteData->target->path, FILE_PATH_MAX); - } else { - string_get_parent_path(baseDstPath, pasteData->target->path, FILE_PATH_MAX); - } - - if(strncmp(parentPath, baseDstPath, FILE_PATH_MAX) == 0) { - list_item* dstItem = NULL; - if(R_SUCCEEDED(res) && R_SUCCEEDED(task_create_file_item(&dstItem, pasteData->target->archive, dstPath, attributes, true))) { - linked_list_add(pasteData->items, dstItem); - } - } - } - - fs_free_path_utf8(fsPath); - } else { - res = R_APP_OUT_OF_MEMORY; - } - - return res; -} - -static Result action_paste_contents_open_src(void* data, u32 index, u32* handle) { - paste_contents_data* pasteData = (paste_contents_data*) data; - - Result res = 0; - - FS_Path* fsPath = fs_make_path_utf8(((file_info*) ((list_item*) linked_list_get(&pasteData->contents, index))->data)->path); - if(fsPath != NULL) { - res = FSUSER_OpenFile(handle, clipboard_get_archive(), *fsPath, FS_OPEN_READ, 0); - - fs_free_path_utf8(fsPath); - } else { - res = R_APP_OUT_OF_MEMORY; - } - - return res; -} - -static Result action_paste_contents_close_src(void* data, u32 index, bool succeeded, u32 handle) { - return FSFILE_Close(handle); -} - -static Result action_paste_contents_get_src_size(void* data, u32 handle, u64* size) { - return FSFILE_GetSize(handle, size); -} - -static Result action_paste_contents_read_src(void* data, u32 handle, u32* bytesRead, void* buffer, u64 offset, u32 size) { - return FSFILE_Read(handle, bytesRead, offset, buffer, size); -} - -static Result action_paste_contents_open_dst(void* data, u32 index, void* initialReadBlock, u64 size, u32* handle) { - paste_contents_data* pasteData = (paste_contents_data*) data; - - Result res = 0; - - char dstPath[FILE_PATH_MAX]; - action_paste_contents_get_dst_path(pasteData, index, dstPath); - - FS_Path* fsPath = fs_make_path_utf8(dstPath); - if(fsPath != NULL) { - Handle currHandle; - if(R_SUCCEEDED(FSUSER_OpenFile(&currHandle, pasteData->target->archive, *fsPath, FS_OPEN_READ, 0))) { - FSFILE_Close(currHandle); - if(R_SUCCEEDED(res = FSUSER_DeleteFile(pasteData->target->archive, *fsPath))) { - linked_list_iter iter; - linked_list_iterate(pasteData->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, dstPath, FILE_PATH_MAX) == 0) { - linked_list_iter_remove(&iter); - task_free_file(item); - } - } - } - } - - if(R_SUCCEEDED(res) && R_SUCCEEDED(res = FSUSER_CreateFile(pasteData->target->archive, *fsPath, ((file_info*) ((list_item*) linked_list_get(&pasteData->contents, index))->data)->attributes & ~FS_ATTRIBUTE_READ_ONLY, size))) { - res = FSUSER_OpenFile(handle, pasteData->target->archive, *fsPath, FS_OPEN_WRITE, 0); - } - - fs_free_path_utf8(fsPath); - } else { - res = R_APP_OUT_OF_MEMORY; - } - - return res; -} - -static Result action_paste_contents_close_dst(void* data, u32 index, bool succeeded, u32 handle) { - paste_contents_data* pasteData = (paste_contents_data*) data; - - Result res = 0; - - if(R_SUCCEEDED(res = FSFILE_Close(handle))) { - char dstPath[FILE_PATH_MAX]; - action_paste_contents_get_dst_path(pasteData, index, dstPath); - - char parentPath[FILE_PATH_MAX]; - string_get_parent_path(parentPath, dstPath, FILE_PATH_MAX); - - char baseDstPath[FILE_PATH_MAX]; - if(pasteData->target->attributes & FS_ATTRIBUTE_DIRECTORY) { - string_copy(baseDstPath, pasteData->target->path, FILE_PATH_MAX); - } else { - string_get_parent_path(baseDstPath, pasteData->target->path, FILE_PATH_MAX); - } - - if(strncmp(parentPath, baseDstPath, FILE_PATH_MAX) == 0) { - list_item* dstItem = NULL; - if(R_SUCCEEDED(task_create_file_item(&dstItem, pasteData->target->archive, dstPath, ((file_info*) ((list_item*) linked_list_get(&pasteData->contents, index))->data)->attributes & ~FS_ATTRIBUTE_READ_ONLY, true))) { - linked_list_add(pasteData->items, dstItem); - } - } - } - - return res; -} - -static Result action_paste_contents_write_dst(void* data, u32 handle, u32* bytesWritten, void* buffer, u64 offset, u32 size) { - return FSFILE_Write(handle, bytesWritten, offset, buffer, size, 0); -} - -static Result action_paste_contents_suspend(void* data, u32 index) { - return 0; -} - -static Result action_paste_contents_restore(void* data, u32 index) { - return 0; -} - -static bool action_paste_contents_error(void* data, u32 index, Result res, ui_view** errorView) { - *errorView = error_display_res(data, action_paste_contents_draw_top, res, "无法粘贴项目."); - return true; -} - -static void action_paste_contents_free_data(paste_contents_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_paste_contents_update(ui_view* view, void* data, float* progress, char* text) { - paste_contents_data* pasteData = (paste_contents_data*) data; - - if(pasteData->pasteInfo.finished) { - FSUSER_ControlArchive(pasteData->target->archive, ARCHIVE_ACTION_COMMIT_SAVE_DATA, NULL, 0, NULL, 0); - - linked_list_sort(pasteData->items, NULL, task_compare_files); - - ui_pop(); - info_destroy(view); - - if(R_SUCCEEDED(pasteData->pasteInfo.result)) { - prompt_display_notify("成功", "已粘贴.", COLOR_TEXT, NULL, NULL, NULL); - } - - action_paste_contents_free_data(pasteData); - - return; - } - - if((hidKeysDown() & KEY_B) && !pasteData->pasteInfo.finished) { - svcSignalEvent(pasteData->pasteInfo.cancelEvent); - } - - *progress = pasteData->pasteInfo.currTotal != 0 ? (float) ((double) pasteData->pasteInfo.currProcessed / (double) pasteData->pasteInfo.currTotal) : 0; - snprintf(text, PROGRESS_TEXT_MAX, "%lu / %lu\n%.2f %s / %.2f %s\n%.2f %s/s, ETA %s", pasteData->pasteInfo.processed, pasteData->pasteInfo.total, - ui_get_display_size(pasteData->pasteInfo.currProcessed), - ui_get_display_size_units(pasteData->pasteInfo.currProcessed), - ui_get_display_size(pasteData->pasteInfo.currTotal), - ui_get_display_size_units(pasteData->pasteInfo.currTotal), - ui_get_display_size(pasteData->pasteInfo.bytesPerSecond), - ui_get_display_size_units(pasteData->pasteInfo.bytesPerSecond), - ui_get_display_eta(pasteData->pasteInfo.estimatedRemainingSeconds)); -} - -static void action_paste_contents_onresponse(ui_view* view, void* data, u32 response) { - paste_contents_data* pasteData = (paste_contents_data*) data; - if(response == PROMPT_YES) { - Result res = task_data_op(&pasteData->pasteInfo); - if(R_SUCCEEDED(res)) { - info_display("正在粘贴", "按 B 取消.", true, data, action_paste_contents_update, action_paste_contents_draw_top); - } else { - error_display_res(NULL, NULL, res, "无法启动粘贴操作."); - - action_paste_contents_free_data(pasteData); - } - } else { - action_paste_contents_free_data(pasteData); - } -} - -typedef struct { - paste_contents_data* pasteData; - - populate_files_data popData; -} paste_contents_loading_data; - -static void action_paste_contents_loading_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - action_paste_contents_draw_top(view, ((paste_contents_loading_data*) data)->pasteData, x1, y1, x2, y2); -} - -static void action_paste_contents_loading_update(ui_view* view, void* data, float* progress, char* text) { - paste_contents_loading_data* loadingData = (paste_contents_loading_data*) data; - - if(loadingData->popData.finished) { - ui_pop(); - info_destroy(view); - - if(R_SUCCEEDED(loadingData->popData.result)) { - loadingData->pasteData->pasteInfo.total = linked_list_size(&loadingData->pasteData->contents); - loadingData->pasteData->pasteInfo.processed = loadingData->pasteData->pasteInfo.total; - - prompt_display_yes_no("确认", "粘贴至当前文件夹?", COLOR_TEXT, loadingData->pasteData, action_paste_contents_draw_top, action_paste_contents_onresponse); - } else { - error_display_res(NULL, NULL, loadingData->popData.result, "无法启动剪贴板项目填充."); - - action_paste_contents_free_data(loadingData->pasteData); - } - - free(loadingData); - return; - } - - if((hidKeysDown() & KEY_B) && !loadingData->popData.finished) { - svcSignalEvent(loadingData->popData.cancelEvent); - } - - snprintf(text, PROGRESS_TEXT_MAX, "正在获取剪贴板项目..."); -} - -void action_paste_contents(linked_list* items, list_item* selected) { - if(!clipboard_has_contents()) { - prompt_display_notify("失败", "剪贴板为空.", COLOR_TEXT, NULL, NULL, NULL); - return; - } - - paste_contents_data* data = (paste_contents_data*) calloc(1, sizeof(paste_contents_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_paste_contents_free_data(data); - return; - } - - data->target = (file_info*) data->targetItem->data; - - data->pasteInfo.data = data; - - data->pasteInfo.op = DATAOP_COPY; - - data->pasteInfo.bufferSize = 256 * 1024; - data->pasteInfo.copyEmpty = true; - - data->pasteInfo.isSrcDirectory = action_paste_contents_is_src_directory; - data->pasteInfo.makeDstDirectory = action_paste_contents_make_dst_directory; - - data->pasteInfo.openSrc = action_paste_contents_open_src; - data->pasteInfo.closeSrc = action_paste_contents_close_src; - data->pasteInfo.getSrcSize = action_paste_contents_get_src_size; - data->pasteInfo.readSrc = action_paste_contents_read_src; - - data->pasteInfo.openDst = action_paste_contents_open_dst; - data->pasteInfo.closeDst = action_paste_contents_close_dst; - data->pasteInfo.writeDst = action_paste_contents_write_dst; - - data->pasteInfo.suspend = action_paste_contents_suspend; - data->pasteInfo.restore = action_paste_contents_restore; - - data->pasteInfo.error = action_paste_contents_error; - - data->pasteInfo.finished = true; - - linked_list_init(&data->contents); - - paste_contents_loading_data* loadingData = (paste_contents_loading_data*) calloc(1, sizeof(paste_contents_loading_data)); - if(loadingData == NULL) { - error_display(NULL, NULL, "无法分配加载的数据."); - - action_paste_contents_free_data(data); - return; - } - - loadingData->pasteData = data; - - loadingData->popData.items = &data->contents; - loadingData->popData.archive = clipboard_get_archive(); - string_copy(loadingData->popData.path, clipboard_get_path(), FILE_PATH_MAX); - loadingData->popData.recursive = true; - loadingData->popData.includeBase = !clipboard_is_contents_only() || !fs_is_dir(clipboard_get_archive(), clipboard_get_path()); - loadingData->popData.meta = false; - loadingData->popData.filter = 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_paste_contents_free_data(data); - return; - } - - info_display("正在加载", "按 B 取消.", false, loadingData, action_paste_contents_loading_update, action_paste_contents_loading_draw_top); -} diff --git a/source/fbi/action/rename.c b/source/fbi/action/rename.c deleted file mode 100644 index f17e4a4..0000000 --- a/source/fbi/action/rename.c +++ /dev/null @@ -1,90 +0,0 @@ -#include -#include -#include - -#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; -} rename_data; - -static void action_rename_onresponse(ui_view* view, void* data, SwkbdButton button, const char* response) { - rename_data* renameData = (rename_data*) data; - - if(button == SWKBD_BUTTON_CONFIRM) { - Result res = 0; - - list_item* selected = renameData->selected; - file_info* targetInfo = (file_info*) selected->data; - - char fileName[FILE_NAME_MAX] = {'\0'}; - string_escape_file_name(fileName, response, sizeof(fileName)); - - char parentPath[FILE_PATH_MAX] = {'\0'}; - string_get_parent_path(parentPath, targetInfo->path, FILE_PATH_MAX); - - char dstPath[FILE_PATH_MAX] = {'\0'}; - if(targetInfo->attributes & FS_ATTRIBUTE_DIRECTORY) { - snprintf(dstPath, FILE_PATH_MAX, "%s%s/", parentPath, fileName); - } else { - snprintf(dstPath, FILE_PATH_MAX, "%s%s", parentPath, fileName); - } - - FS_Path* srcFsPath = fs_make_path_utf8(targetInfo->path); - if(srcFsPath != NULL) { - FS_Path* dstFsPath = fs_make_path_utf8(dstPath); - if(dstFsPath != NULL) { - if(targetInfo->attributes & FS_ATTRIBUTE_DIRECTORY) { - res = FSUSER_RenameDirectory(targetInfo->archive, *srcFsPath, targetInfo->archive, *dstFsPath); - } else { - res = FSUSER_RenameFile(targetInfo->archive, *srcFsPath, targetInfo->archive, *dstFsPath); - } - - fs_free_path_utf8(dstFsPath); - } else { - res = R_APP_OUT_OF_MEMORY; - } - - fs_free_path_utf8(srcFsPath); - } else { - res = R_APP_OUT_OF_MEMORY; - } - - if(R_SUCCEEDED(res)) { - if(strncmp(selected->name, "<当前文件夹>", LIST_ITEM_NAME_MAX) != 0 && strncmp(selected->name, "<当前文件>", LIST_ITEM_NAME_MAX) != 0) { - string_copy(selected->name, fileName, LIST_ITEM_NAME_MAX); - } - - string_copy(targetInfo->name, fileName, FILE_NAME_MAX); - string_copy(targetInfo->path, dstPath, FILE_PATH_MAX); - - linked_list_sort(renameData->items, NULL, task_compare_files); - - prompt_display_notify("成功", "已重命名.", COLOR_TEXT, NULL, NULL, NULL); - } else { - error_display_res(NULL, NULL, res, "无法执行重命名."); - } - } - - free(renameData); -} - -void action_rename(linked_list* items, list_item* selected) { - rename_data* data = (rename_data*) calloc(1, sizeof(rename_data)); - if(data == NULL) { - error_display(NULL, NULL, "无法分配重命名的数据."); - - return; - } - - data->items = items; - data->selected = selected; - - kbd_display("输入新名称", ((file_info*) selected->data)->name, SWKBD_TYPE_NORMAL, 0, SWKBD_NOTEMPTY_NOTBLANK, FILE_NAME_MAX, data, action_rename_onresponse); -} diff --git a/source/fbi/dumpnand.c b/source/fbi/dumpnand.c deleted file mode 100644 index b23391a..0000000 --- a/source/fbi/dumpnand.c +++ /dev/null @@ -1,168 +0,0 @@ -#include -#include -#include -#include - -#include <3ds.h> - -#include "resources.h" -#include "section.h" -#include "task/uitask.h" -#include "../core/core.h" - -static Result dumpnand_is_src_directory(void* data, u32 index, bool* isDirectory) { - *isDirectory = false; - return 0; -} - -static Result dumpnand_make_dst_directory(void* data, u32 index) { - return 0; -} - -static Result dumpnand_open_src(void* data, u32 index, u32* handle) { - return FSUSER_OpenFileDirectly(handle, ARCHIVE_NAND_W_FS, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_UTF16, u"/"), FS_OPEN_READ, 0); -} - -static Result dumpnand_close_src(void* data, u32 index, bool succeeded, u32 handle) { - return FSFILE_Close(handle); -} - -static Result dumpnand_get_src_size(void* data, u32 handle, u64* size) { - return FSFILE_GetSize(handle, size); -} - -static Result dumpnand_read_src(void* data, u32 handle, u32* bytesRead, void* buffer, u64 offset, u32 size) { - return FSFILE_Read(handle, bytesRead, offset, buffer, size); -} - -static Result dumpnand_open_dst(void* data, u32 index, void* initialReadBlock, u64 size, u32* handle) { - Result res = 0; - - FS_Archive sdmcArchive = 0; - if(R_SUCCEEDED(res = FSUSER_OpenArchive(&sdmcArchive, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")))) { - if(R_SUCCEEDED(res = fs_ensure_dir(sdmcArchive, "/fbi/")) && R_SUCCEEDED(res = fs_ensure_dir(sdmcArchive, "/fbi/nand/"))) { - time_t t = time(NULL); - struct tm* timeInfo = localtime(&t); - - char path[FILE_PATH_MAX]; - strftime(path, sizeof(path), "/fbi/nand/NAND_%m-%d-%y_%H-%M-%S.bin", timeInfo); - - FS_Path* fsPath = fs_make_path_utf8(path); - if(fsPath != NULL) { - res = FSUSER_OpenFileDirectly(handle, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), *fsPath, FS_OPEN_WRITE | FS_OPEN_CREATE, 0); - - fs_free_path_utf8(fsPath); - } else { - res = R_APP_OUT_OF_MEMORY; - } - } - - FSUSER_CloseArchive(sdmcArchive); - } - - return res; -} - -static Result dumpnand_close_dst(void* data, u32 index, bool succeeded, u32 handle) { - return FSFILE_Close(handle); -} - -static Result dumpnand_write_dst(void* data, u32 handle, u32* bytesWritten, void* buffer, u64 offset, u32 size) { - return FSFILE_Write(handle, bytesWritten, offset, buffer, size, 0); -} - -static Result dumpnand_suspend(void* data, u32 index) { - return 0; -} - -static Result dumpnand_restore(void* data, u32 index) { - return 0; -} - -static bool dumpnand_error(void* data, u32 index, Result res, ui_view** errorView) { - *errorView = error_display_res(NULL, NULL, res, "无法导出 NAND."); - return true; -} - -static void dumpnand_update(ui_view* view, void* data, float* progress, char* text) { - data_op_data* dumpData = (data_op_data*) data; - - if(dumpData->finished) { - ui_pop(); - info_destroy(view); - - if(R_SUCCEEDED(dumpData->result)) { - prompt_display_notify("成功", "已导出.", COLOR_TEXT, NULL, NULL, NULL); - } - - free(dumpData); - - return; - } - - if(hidKeysDown() & KEY_B) { - svcSignalEvent(dumpData->cancelEvent); - } - - *progress = dumpData->currTotal != 0 ? (float) ((double) dumpData->currProcessed / (double) dumpData->currTotal) : 0; - snprintf(text, PROGRESS_TEXT_MAX, "%.2f %s / %.2f %s\n%.2f %s/s, ETA %s", - ui_get_display_size(dumpData->currProcessed), ui_get_display_size_units(dumpData->currProcessed), - ui_get_display_size(dumpData->currTotal), ui_get_display_size_units(dumpData->currTotal), - ui_get_display_size(dumpData->bytesPerSecond), ui_get_display_size_units(dumpData->bytesPerSecond), - ui_get_display_eta(dumpData->estimatedRemainingSeconds)); -} - -static void dumpnand_onresponse(ui_view* view, void* data, u32 response) { - if(response == PROMPT_YES) { - data_op_data* dumpData = (data_op_data*) data; - - Result res = task_data_op(dumpData); - if(R_SUCCEEDED(res)) { - info_display("正在导出", "按 B 取消.", true, data, dumpnand_update, NULL); - } else { - error_display_res(NULL, NULL, res, "无法启动导出 NAND."); - free(data); - } - } else { - free(data); - } -} - -void dumpnand_open() { - data_op_data* data = (data_op_data*) calloc(1, sizeof(data_op_data)); - if(data == NULL) { - error_display(NULL, NULL, "无法分配导出 NAND 的数据."); - - return; - } - - data->data = data; - - data->op = DATAOP_COPY; - - data->bufferSize = 256 * 1024; - data->copyEmpty = true; - - data->total = 1; - - data->isSrcDirectory = dumpnand_is_src_directory; - data->makeDstDirectory = dumpnand_make_dst_directory; - - data->openSrc = dumpnand_open_src; - data->closeSrc = dumpnand_close_src; - data->getSrcSize = dumpnand_get_src_size; - data->readSrc = dumpnand_read_src; - - data->openDst = dumpnand_open_dst; - data->closeDst = dumpnand_close_dst; - data->writeDst = dumpnand_write_dst; - - data->suspend = dumpnand_suspend; - data->restore = dumpnand_restore; - - data->error = dumpnand_error; - - data->finished = true; - - prompt_display_yes_no("确认", "导出原始 NAND 镜像至 SD 卡?", COLOR_TEXT, data, NULL, dumpnand_onresponse); -} diff --git a/source/fbi/extsavedata.c b/source/fbi/extsavedata.c deleted file mode 100644 index 1ad0605..0000000 --- a/source/fbi/extsavedata.c +++ /dev/null @@ -1,275 +0,0 @@ -#include -#include -#include - -#include <3ds.h> - -#include "resources.h" -#include "section.h" -#include "action/action.h" -#include "task/uitask.h" -#include "../core/core.h" - -static list_item browse_user_save_data = {"浏览用户数据", COLOR_TEXT, action_browse_user_ext_save_data}; -static list_item browse_spotpass_save_data = {"浏览悄然连接数据", COLOR_TEXT, action_browse_boss_ext_save_data}; -static list_item delete_save_data = {"删除数据", COLOR_TEXT, action_delete_ext_save_data}; - -typedef struct { - populate_ext_save_data_data populateData; - - bool showSD; - bool showNAND; - bool sortById; - bool sortByName; - - bool populated; -} extsavedata_data; - -typedef struct { - linked_list* items; - list_item* selected; -} extsavedata_action_data; - -static void extsavedata_action_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { - task_draw_ext_save_data_info(view, ((extsavedata_action_data*) data)->selected->data, x1, y1, x2, y2); -} - -static void extsavedata_action_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { - extsavedata_action_data* actionData = (extsavedata_action_data*) data; - - if(hidKeysDown() & KEY_B) { - ui_pop(); - list_destroy(view); - - free(data); - - return; - } - - if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { - void(*action)(linked_list*, list_item*) = (void(*)(linked_list*, list_item*)) selected->data; - - ui_pop(); - list_destroy(view); - - action(actionData->items, actionData->selected); - - free(data); - - return; - } - - if(linked_list_size(items) == 0) { - linked_list_add(items, &browse_user_save_data); - linked_list_add(items, &browse_spotpass_save_data); - linked_list_add(items, &delete_save_data); - } -} - -static void extsavedata_action_open(linked_list* items, list_item* selected) { - extsavedata_action_data* data = (extsavedata_action_data*) calloc(1, sizeof(extsavedata_action_data)); - if(data == NULL) { - error_display(NULL, NULL, "无法分配追加数据操作的数据."); - - return; - } - - data->items = items; - data->selected = selected; - - list_display("追加数据操作", "A: 选择, B: 返回", data, extsavedata_action_update, extsavedata_action_draw_top); -} - -static void extsavedata_options_add_entry(linked_list* items, const char* name, bool* val) { - list_item* item = (list_item*) calloc(1, sizeof(list_item)); - if(item != NULL) { - snprintf(item->name, LIST_ITEM_NAME_MAX, "%s", name); - item->color = *val ? COLOR_ENABLED : COLOR_DISABLED; - item->data = val; - - linked_list_add(items, item); - } -} - -static void extsavedata_options_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { - extsavedata_data* listData = (extsavedata_data*) data; - - if(hidKeysDown() & KEY_B) { - linked_list_iter iter; - linked_list_iterate(items, &iter); - - while(linked_list_iter_has_next(&iter)) { - free(linked_list_iter_next(&iter)); - linked_list_iter_remove(&iter); - } - - ui_pop(); - list_destroy(view); - - return; - } - - if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { - bool* val = (bool*) selected->data; - *val = !(*val); - - if(*val && (val == &listData->sortById || val == &listData->sortByName)) { - if(val == &listData->sortById) { - listData->sortByName = false; - } else if(val == &listData->sortByName) { - listData->sortById = false; - } - - linked_list_iter iter; - linked_list_iterate(items, &iter); - while(linked_list_iter_has_next(&iter)) { - list_item* item = (list_item*) linked_list_iter_next(&iter); - - item->color = *(bool*) item->data ? COLOR_ENABLED : COLOR_DISABLED; - } - } else { - selected->color = *val ? COLOR_ENABLED : COLOR_DISABLED; - } - - listData->populated = false; - } - - if(linked_list_size(items) == 0) { - extsavedata_options_add_entry(items, "显示 SD 卡中的数据", &listData->showSD); - extsavedata_options_add_entry(items, "显示 NAND 中的数据", &listData->showNAND); - extsavedata_options_add_entry(items, "按 ID 排序", &listData->sortById); - extsavedata_options_add_entry(items, "按名称排序", &listData->sortByName); - } -} - -static void extsavedata_options_open(extsavedata_data* data) { - list_display("选项", "A: 切换, B: 返回", data, extsavedata_options_update, NULL); -} - -static void extsavedata_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { - if(selected != NULL && selected->data != NULL) { - task_draw_ext_save_data_info(view, selected->data, x1, y1, x2, y2); - } -} - -static void extsavedata_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { - extsavedata_data* listData = (extsavedata_data*) data; - - if(hidKeysDown() & KEY_B) { - if(!listData->populateData.finished) { - svcSignalEvent(listData->populateData.cancelEvent); - while(!listData->populateData.finished) { - svcSleepThread(1000000); - } - } - - ui_pop(); - - task_clear_ext_save_data(items); - list_destroy(view); - - free(listData); - return; - } - - if(hidKeysDown() & KEY_SELECT) { - extsavedata_options_open(listData); - return; - } - - if(!listData->populated || (hidKeysDown() & KEY_X)) { - if(!listData->populateData.finished) { - svcSignalEvent(listData->populateData.cancelEvent); - while(!listData->populateData.finished) { - svcSleepThread(1000000); - } - } - - listData->populateData.items = items; - Result res = task_populate_ext_save_data(&listData->populateData); - if(R_FAILED(res)) { - error_display_res(NULL, NULL, res, "无法启动追加数据列表填充."); - } - - listData->populated = true; - } - - if(listData->populateData.finished && R_FAILED(listData->populateData.result)) { - error_display_res(NULL, NULL, listData->populateData.result, "无法填充追加数据列表."); - - listData->populateData.result = 0; - } - - if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { - extsavedata_action_open(items, selected); - return; - } -} - -static bool extsavedata_filter(void* data, u64 titleId, FS_MediaType mediaType) { - extsavedata_data* listData = (extsavedata_data*) data; - - if(mediaType == MEDIATYPE_SD) { - return listData->showSD; - } else { - return listData->showNAND; - } -} - -static int extsavedata_compare(void* data, const void* p1, const void* p2) { - extsavedata_data* listData = (extsavedata_data*) data; - - list_item* info1 = (list_item*) p1; - list_item* info2 = (list_item*) p2; - - ext_save_data_info* data1 = (ext_save_data_info*) info1->data; - ext_save_data_info* data2 = (ext_save_data_info*) info2->data; - - if(data1->mediaType > data2->mediaType) { - return -1; - } else if(data1->mediaType < data2->mediaType) { - return 1; - } else { - if(listData->sortById) { - u64 id1 = data1->extSaveDataId; - u64 id2 = data2->extSaveDataId; - - return id1 > id2 ? 1 : id1 < id2 ? -1 : 0; - } else if(listData->sortByName) { - bool title1HasName = data1->hasMeta && !string_is_empty(data1->meta.shortDescription); - bool title2HasName = data2->hasMeta && !string_is_empty(data2->meta.shortDescription); - - if(title1HasName && !title2HasName) { - return -1; - } else if(!title1HasName && title2HasName) { - return 1; - } else { - return strncasecmp(info1->name, info2->name, sizeof(info1->name)); - } - } else { - return 0; - } - } -} - -void extsavedata_open() { - extsavedata_data* data = (extsavedata_data*) calloc(1, sizeof(extsavedata_data)); - if(data == NULL) { - error_display(NULL, NULL, "无法分配追加数据的数据."); - - return; - } - - data->populateData.userData = data; - data->populateData.filter = extsavedata_filter; - data->populateData.compare = extsavedata_compare; - - data->populateData.finished = true; - - data->showSD = true; - data->showNAND = true; - data->sortById = false; - data->sortByName = true; - - list_display("追加数据", "A: 选择, B: 返回, X: 刷新, SELECT: 选项", data, extsavedata_update, extsavedata_draw_top); -} diff --git a/source/fbi/files.c b/source/fbi/files.c deleted file mode 100644 index f203f13..0000000 --- a/source/fbi/files.c +++ /dev/null @@ -1,454 +0,0 @@ -#include -#include -#include - -#include <3ds.h> - -#include "resources.h" -#include "section.h" -#include "action/action.h" -#include "task/uitask.h" -#include "../core/core.h" - -static list_item rename_opt = {"重命名", COLOR_TEXT, action_rename}; -static list_item copy = {"复制", COLOR_TEXT, NULL}; -static list_item paste = {"粘贴", COLOR_TEXT, action_paste_contents}; - -static list_item delete_file = {"删除", COLOR_TEXT, action_delete_file}; - -static list_item install_cia = {"安装安装包", COLOR_TEXT, action_install_cia}; -static list_item install_and_delete_cia = {"安装并删除安装包", COLOR_TEXT, action_install_cia_delete}; - -static list_item install_ticket = {"安装应用引导表", COLOR_TEXT, action_install_ticket}; -static list_item install_and_delete_ticket = {"安装并删除应用引导表", COLOR_TEXT, action_install_ticket_delete}; - -static list_item delete_dir = {"删除", COLOR_TEXT, action_delete_dir}; -static list_item copy_all_contents = {"复制所有项目", COLOR_TEXT, NULL}; -static list_item delete_all_contents = {"删除所有项目", COLOR_TEXT, action_delete_dir_contents}; -static list_item new_folder = {"新建文件夹", COLOR_TEXT, action_new_folder}; - -static list_item install_all_cias = {"安装所有安装包", COLOR_TEXT, action_install_cias}; -static list_item install_and_delete_all_cias = {"安装并删除所有安装包", COLOR_TEXT, action_install_cias_delete}; -static list_item delete_all_cias = {"删除安装包", COLOR_TEXT, action_delete_dir_cias}; - -static list_item install_all_tickets = {"安装所有应用引导表", COLOR_TEXT, action_install_tickets}; -static list_item install_and_delete_all_tickets = {"安装并删除所有应用引导表", COLOR_TEXT, action_install_tickets_delete}; -static list_item delete_all_tickets = {"删除所有应用引导表", COLOR_TEXT, action_delete_dir_tickets}; - -typedef struct { - populate_files_data populateData; - - bool populated; - - FS_ArchiveID archiveId; - FS_Path archivePath; - FS_Archive archive; - - bool showHidden; - bool showDirectories; - bool showFiles; - bool showCias; - bool showTickets; - - char currDir[FILE_PATH_MAX]; -} files_data; - -typedef struct { - linked_list* items; - list_item* selected; - files_data* parent; - - bool containsCias; - bool containsTickets; -} files_action_data; - -static bool files_filter(void* data, const char* name, u32 attributes) { - files_data* listData = (files_data*) data; - - if((attributes & FS_ATTRIBUTE_HIDDEN) != 0 && !listData->showHidden) { - return false; - } - - if((attributes & FS_ATTRIBUTE_DIRECTORY) != 0) { - return listData->showDirectories; - } else { - if((fs_filter_cias(NULL, name, attributes) && !listData->showCias) || (fs_filter_tickets(NULL, name, attributes) && !listData->showTickets)) { - return false; - } - - return listData->showFiles; - } -} - -static void files_action_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { - task_draw_file_info(view, ((files_action_data*) data)->selected->data, x1, y1, x2, y2); -} - -static void files_action_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { - files_action_data* actionData = (files_action_data*) data; - - if(hidKeysDown() & KEY_B) { - ui_pop(); - list_destroy(view); - - free(data); - - return; - } - - if(selected != NULL && (selected->data != NULL || selected == © || selected == ©_all_contents) && (selectedTouched || (hidKeysDown() & KEY_A))) { - void* action = selected->data; - - ui_pop(); - list_destroy(view); - - if(selected == © || selected == ©_all_contents) { - file_info* info = (file_info*) actionData->selected->data; - - Result res = 0; - if(R_SUCCEEDED(res = clipboard_set_contents(actionData->parent->archive, info->path, selected == ©_all_contents))) { - prompt_display_notify("成功", selected == ©_all_contents ? "当前文件夹的所有项目已复制到剪贴板." : (info->attributes & FS_ATTRIBUTE_DIRECTORY) ? "当前文件夹已复制到剪贴板." : "文件已复制到剪贴板.", COLOR_TEXT, info, task_draw_file_info, NULL); - } else { - error_display_res(info, task_draw_file_info, res, "无法复制到剪贴板."); - } - } else if(selected == &install_all_cias || selected == &install_and_delete_all_cias || selected == &install_all_tickets || selected == &install_and_delete_all_tickets) { - void (*filteredAction)(linked_list*, list_item*, bool (*)(void*, const char*, u32), void*) = action; - - filteredAction(actionData->items, actionData->selected, files_filter, actionData->parent); - } else { - void (*normalAction)(linked_list*, list_item*) = action; - - normalAction(actionData->items, actionData->selected); - } - - free(data); - - return; - } - - if(linked_list_size(items) == 0) { - file_info* info = (file_info*) actionData->selected->data; - - if(info->attributes & FS_ATTRIBUTE_DIRECTORY) { - if(actionData->containsCias) { - linked_list_add(items, &install_all_cias); - linked_list_add(items, &install_and_delete_all_cias); - linked_list_add(items, &delete_all_cias); - } - - if(actionData->containsTickets) { - linked_list_add(items, &install_all_tickets); - linked_list_add(items, &install_and_delete_all_tickets); - linked_list_add(items, &delete_all_tickets); - } - - linked_list_add(items, ©_all_contents); - linked_list_add(items, &delete_all_contents); - - linked_list_add(items, &new_folder); - - linked_list_add(items, &delete_dir); - } else { - if(info->isCia) { - linked_list_add(items, &install_cia); - linked_list_add(items, &install_and_delete_cia); - } - - if(info->isTicket) { - linked_list_add(items, &install_ticket); - linked_list_add(items, &install_and_delete_ticket); - } - - linked_list_add(items, &delete_file); - } - - linked_list_add(items, &rename_opt); - linked_list_add(items, ©); - linked_list_add(items, &paste); - } -} - -static void files_action_open(linked_list* items, list_item* selected, files_data* parent) { - files_action_data* data = (files_action_data*) calloc(1, sizeof(files_action_data)); - if(data == NULL) { - error_display(NULL, NULL, "无法分配文件操作的数据."); - - return; - } - - data->items = items; - data->selected = selected; - data->parent = parent; - - data->containsCias = false; - data->containsTickets = false; - - linked_list_iter iter; - linked_list_iterate(data->items, &iter); - - while(linked_list_iter_has_next(&iter)) { - file_info* info = (file_info*) ((list_item*) linked_list_iter_next(&iter))->data; - - if(info->isCia) { - data->containsCias = true; - } else if(info->isTicket) { - data->containsTickets = true; - } - } - - list_display((((file_info*) selected->data)->attributes & FS_ATTRIBUTE_DIRECTORY) ? "文件夹操作" : "文件操作", "A: 选择, B: 返回", data, files_action_update, files_action_draw_top); -} - -static void files_options_add_entry(linked_list* items, const char* name, bool* val) { - list_item* item = (list_item*) calloc(1, sizeof(list_item)); - if(item != NULL) { - snprintf(item->name, LIST_ITEM_NAME_MAX, "%s", name); - item->color = *val ? COLOR_ENABLED : COLOR_DISABLED; - item->data = val; - - linked_list_add(items, item); - } -} - -static void files_options_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { - files_data* listData = (files_data*) data; - - if(hidKeysDown() & KEY_B) { - linked_list_iter iter; - linked_list_iterate(items, &iter); - - while(linked_list_iter_has_next(&iter)) { - free(linked_list_iter_next(&iter)); - linked_list_iter_remove(&iter); - } - - ui_pop(); - list_destroy(view); - - return; - } - - if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { - bool* val = (bool*) selected->data; - *val = !(*val); - - selected->color = *val ? COLOR_ENABLED : COLOR_DISABLED; - - listData->populated = false; - } - - if(linked_list_size(items) == 0) { - files_options_add_entry(items, "显示隐藏的项目", &listData->showHidden); - files_options_add_entry(items, "显示文件夹", &listData->showDirectories); - files_options_add_entry(items, "显示文件", &listData->showFiles); - files_options_add_entry(items, "显示安装包", &listData->showCias); - files_options_add_entry(items, "显示应用引导表", &listData->showTickets); - } -} - -static void files_options_open(files_data* data) { - list_display("选项", "A: 切换, B: 返回", data, files_options_update, NULL); -} - -static void files_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { - if(selected != NULL && selected->data != NULL) { - task_draw_file_info(view, selected->data, x1, y1, x2, y2); - } -} - -static void files_repopulate(files_data* listData, linked_list* items) { - if(!listData->populateData.finished) { - svcSignalEvent(listData->populateData.cancelEvent); - while(!listData->populateData.finished) { - svcSleepThread(1000000); - } - } - - listData->populateData.items = items; - listData->populateData.archive = listData->archive; - string_copy(listData->populateData.path, listData->currDir, FILE_PATH_MAX); - - Result res = task_populate_files(&listData->populateData); - if(R_FAILED(res)) { - error_display_res(NULL, NULL, res, "无法启动文件列表填充."); - } - - listData->populated = true; -} - -static void files_navigate(files_data* listData, linked_list* items, const char* path) { - string_copy(listData->currDir, path, FILE_PATH_MAX); - - listData->populated = false; -} - -static void files_free_data(files_data* data) { - if(!data->populateData.finished) { - svcSignalEvent(data->populateData.cancelEvent); - while(!data->populateData.finished) { - svcSleepThread(1000000); - } - } - - if(data->archive != 0) { - fs_close_archive(data->archive); - data->archive = 0; - } - - if(data->archivePath.data != NULL) { - free((void*) data->archivePath.data); - data->archivePath.data = NULL; - } - - free(data); -} - -static void files_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { - files_data* listData = (files_data*) data; - - if(listData->populated) { - // Detect whether the current directory was renamed by an action. - list_item* currDirItem = linked_list_get(items, 0); - if(currDirItem != NULL && strncmp(listData->currDir, ((file_info*) currDirItem->data)->path, FILE_PATH_MAX) != 0) { - string_copy(listData->currDir, ((file_info*) currDirItem->data)->path, FILE_PATH_MAX); - } - } - - while(!fs_is_dir(listData->archive, listData->currDir)) { - char parentDir[FILE_PATH_MAX] = {'\0'}; - string_get_parent_path(parentDir, listData->currDir, FILE_PATH_MAX); - - files_navigate(listData, items, parentDir); - } - - if(hidKeysDown() & KEY_B) { - if(strncmp(listData->currDir, "/", FILE_PATH_MAX) == 0) { - ui_pop(); - - files_free_data(listData); - - task_clear_files(items); - list_destroy(view); - - return; - } else { - char parentDir[FILE_PATH_MAX] = {'\0'}; - string_get_parent_path(parentDir, listData->currDir, FILE_PATH_MAX); - - files_navigate(listData, items, parentDir); - } - } - - if(hidKeysDown() & KEY_SELECT) { - files_options_open(listData); - return; - } - - if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { - file_info* fileInfo = (file_info*) selected->data; - - if((fileInfo->attributes & FS_ATTRIBUTE_DIRECTORY) && strncmp(selected->name, "<当前文件夹>", LIST_ITEM_NAME_MAX) != 0) { - files_navigate(listData, items, fileInfo->path); - } else { - files_action_open(items, selected, listData); - return; - } - } - - if(!listData->populated || (hidKeysDown() & KEY_X)) { - files_repopulate(listData, items); - } - - if(listData->populateData.finished && R_FAILED(listData->populateData.result)) { - error_display_res(NULL, NULL, listData->populateData.result, "无法填充文件列表."); - - listData->populateData.result = 0; - } -} - -void files_open(FS_ArchiveID archiveId, FS_Path archivePath) { - files_data* data = (files_data*) calloc(1, sizeof(files_data)); - if(data == NULL) { - error_display(NULL, NULL, "无法分配文件的数据."); - - return; - } - - data->populateData.recursive = false; - data->populateData.includeBase = true; - data->populateData.meta = true; - - data->populateData.filter = files_filter; - data->populateData.filterData = data; - - data->populateData.finished = true; - - data->populated = false; - - data->showHidden = false; - data->showDirectories = true; - data->showFiles = true; - data->showCias = true; - data->showTickets = true; - - data->archiveId = archiveId; - data->archivePath.type = archivePath.type; - data->archivePath.size = archivePath.size; - if(archivePath.data != NULL) { - data->archivePath.data = calloc(1, data->archivePath.size); - if(data->archivePath.data == NULL) { - error_display(NULL, NULL, "无法分配文件的数据."); - - files_free_data(data); - return; - } - - memcpy((void*) data->archivePath.data, archivePath.data, data->archivePath.size); - } else { - data->archivePath.data = NULL; - } - - snprintf(data->currDir, FILE_PATH_MAX, "/"); - - Result res = 0; - if(R_FAILED(res = fs_open_archive(&data->archive, archiveId, archivePath))) { - error_display_res(NULL, NULL, res, "没有找到文件夹."); - - files_free_data(data); - return; - } - - list_display("文件", "A: 选择, B: 返回, X: 刷新, SELECT: 选项", data, files_update, files_draw_top); -} - -static void files_open_nand_warning_onresponse(ui_view* view, void* data, u32 response) { - FS_ArchiveID archive = (FS_ArchiveID) data; - - if(response == PROMPT_YES) { - files_open(archive, fsMakePath(PATH_EMPTY, "")); - } -} - -void files_open_nand_warning(FS_ArchiveID archive) { - prompt_display_yes_no("确认", "修改 系统闪存分区 (NAND) 是危险的,\n这可能导致系统无法正常运行,\n请您确保自己知道正在做什么.\n\n继续?", COLOR_TEXT, (void*) archive, NULL, files_open_nand_warning_onresponse); -} - -void files_open_sd() { - files_open(ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")); -} - -void files_open_ctr_nand() { - files_open_nand_warning(ARCHIVE_NAND_CTR_FS); -} - -void files_open_twl_nand() { - files_open_nand_warning(ARCHIVE_NAND_TWL_FS); -} - -void files_open_twl_photo() { - files_open(ARCHIVE_TWL_PHOTO, fsMakePath(PATH_EMPTY, "")); -} - -void files_open_twl_sound() { - files_open(ARCHIVE_TWL_SOUND, fsMakePath(PATH_EMPTY, "")); -} diff --git a/source/fbi/main.c b/source/fbi/main.c deleted file mode 100644 index 14c1ee7..0000000 --- a/source/fbi/main.c +++ /dev/null @@ -1,189 +0,0 @@ -#include -#include - -#include <3ds.h> - -#include "../core/clipboard.h" -#include "../core/error.h" -#include "../core/fs.h" -#include "../core/screen.h" -#include "../core/task/task.h" -#include "../core/ui/ui.h" -#include "section.h" -#include "task/uitask.h" - -#define CURRENT_KPROCESS (*(void**) 0xFFFF9004) - -#define KPROCESS_PID_OFFSET_OLD (0xB4) -#define KPROCESS_PID_OFFSET_NEW (0xBC) - -static bool backdoor_ran = false; -static bool n3ds = false; -static u32 old_pid = 0; - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wreturn-type" -static __attribute__((naked)) Result svcGlobalBackdoor(s32 (*callback)()) { - asm volatile( - "svc 0x30\n" - "bx lr" - ); -} -#pragma GCC diagnostic pop - -static s32 patch_pid_kernel() { - u32 *pidPtr = (u32*) (CURRENT_KPROCESS + (n3ds ? KPROCESS_PID_OFFSET_NEW : KPROCESS_PID_OFFSET_OLD)); - - old_pid = *pidPtr; - *pidPtr = 0; - - backdoor_ran = true; - return 0; -} - -static s32 restore_pid_kernel() { - u32 *pidPtr = (u32*) (CURRENT_KPROCESS + (n3ds ? KPROCESS_PID_OFFSET_NEW : KPROCESS_PID_OFFSET_OLD)); - - *pidPtr = old_pid; - - backdoor_ran = true; - return 0; -} - -static bool attempt_patch_pid() { - backdoor_ran = false; - APT_CheckNew3DS(&n3ds); - - svcGlobalBackdoor(patch_pid_kernel); - srvExit(); - srvInit(); - svcGlobalBackdoor(restore_pid_kernel); - - return backdoor_ran; -} - -static void (*exit_funcs[16])()= {NULL}; -static u32 exit_func_count = 0; - -static void* soc_buffer = NULL; - -void cleanup_services() { - for(u32 i = 0; i < exit_func_count; i++) { - if(exit_funcs[i] != NULL) { - exit_funcs[i](); - exit_funcs[i] = NULL; - } - } - - exit_func_count = 0; - - if(soc_buffer != NULL) { - free(soc_buffer); - soc_buffer = NULL; - } -} - -#define INIT_SERVICE(initStatement, exitFunc) (R_SUCCEEDED(res = (initStatement)) && (exit_funcs[exit_func_count++] = (exitFunc))) - -Result init_services() { - Result res = 0; - - soc_buffer = memalign(0x1000, 0x100000); - if(soc_buffer != NULL) { - Handle tempAM = 0; - if(R_SUCCEEDED(res = srvGetServiceHandle(&tempAM, "am:net"))) { - svcCloseHandle(tempAM); - - if(INIT_SERVICE(amInit(), amExit) - && INIT_SERVICE(cfguInit(), cfguExit) - && INIT_SERVICE(acInit(), acExit) - && INIT_SERVICE(ptmuInit(), ptmuExit) - && INIT_SERVICE(pxiDevInit(), pxiDevExit) - && INIT_SERVICE(httpcInit(0), httpcExit) - && INIT_SERVICE(socInit(soc_buffer, 0x100000), (void (*)()) socExit)); - } - } else { - res = R_APP_OUT_OF_MEMORY; - } - - if(R_FAILED(res)) { - cleanup_services(); - } - - return res; -} - -static u32 old_time_limit = UINT32_MAX; - -void init() { - gfxInitDefault(); - - Result romfsRes = romfsInit(); - if(R_FAILED(romfsRes)) { - error_panic("无法挂载 RomFS: %08lX", romfsRes); - return; - } - - if(R_FAILED(init_services())) { - if(!attempt_patch_pid()) { - error_panic("内核破解未安装.\n请运行内核破解并重试."); - return; - } - - Result initRes = init_services(); - if(R_FAILED(initRes)) { - error_panic("无法初始化服务: %08lX", initRes); - return; - } - } - - osSetSpeedupEnable(true); - - APT_GetAppCpuTimeLimit(&old_time_limit); - Result cpuRes = APT_SetAppCpuTimeLimit(30); - if(R_FAILED(cpuRes)) { - error_panic("无法设置系统核心 CPU 时间限制: %08lX", cpuRes); - return; - } - - AM_InitializeExternalTitleDatabase(false); - - screen_init(); - ui_init(); - task_init(); -} - -void cleanup() { - clipboard_clear(); - - task_exit(); - ui_exit(); - screen_exit(); - - if(old_time_limit != UINT32_MAX) { - APT_SetAppCpuTimeLimit(old_time_limit); - } - - osSetSpeedupEnable(false); - - cleanup_services(); - - romfsExit(); - - gfxExit(); -} - -int main(int argc, const char* argv[]) { - if(argc > 0 && envIsHomebrew()) { - fs_set_3dsx_path(argv[0]); - } - - init(); - - mainmenu_open(); - while(aptMainLoop() && ui_update()); - - cleanup(); - - return 0; -} diff --git a/source/fbi/mainmenu.c b/source/fbi/mainmenu.c deleted file mode 100644 index 1cda2f0..0000000 --- a/source/fbi/mainmenu.c +++ /dev/null @@ -1,68 +0,0 @@ -#include -#include - -#include <3ds.h> - -#include "resources.h" -#include "section.h" -#include "../core/core.h" - -static list_item sd = {"(Micro) SD 卡", COLOR_TEXT, files_open_sd}; -static list_item ctr_nand = {"3DS 闪存分区 (CTR NAND)", COLOR_TEXT, files_open_ctr_nand}; -static list_item twl_nand = {"DSi 闪存分区 (TWL NAND)", COLOR_TEXT, files_open_twl_nand}; -static list_item twl_photo = {"DSi 照片区", COLOR_TEXT, files_open_twl_photo}; -static list_item twl_sound = {"DSi 录音区", COLOR_TEXT, files_open_twl_sound}; -static list_item dump_nand = {"导出 NAND", COLOR_TEXT, dumpnand_open}; -static list_item titles = {"应用", COLOR_TEXT, titles_open}; -static list_item pending_titles = {"未完成的应用", COLOR_TEXT, pendingtitles_open}; -static list_item tickets = {"应用引导表", COLOR_TEXT, tickets_open}; -static list_item ext_save_data = {"追加数据", COLOR_TEXT, extsavedata_open}; -static list_item system_save_data = {"系统数据", COLOR_TEXT, systemsavedata_open}; -static list_item remote_install = {"远程安装", COLOR_TEXT, remoteinstall_open}; -static list_item update = {"检查更新", COLOR_TEXT, update_open}; - -static void mainmenu_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { - u32 logoWidth; - u32 logoHeight; - screen_get_texture_size(&logoWidth, &logoHeight, TEXTURE_LOGO); - - float logoX = x1 + (x2 - x1 - logoWidth) / 2; - float logoY = y1 + (y2 - y1 - logoHeight) / 2; - screen_draw_texture(TEXTURE_LOGO, logoX, logoY, logoWidth, logoHeight); -} - -static void mainmenu_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { - if(hidKeysDown() & KEY_START) { - ui_pop(); - list_destroy(view); - - return; - } - - if(selected != NULL && (selectedTouched || hidKeysDown() & KEY_A) && selected->data != NULL) { - ((void(*)()) selected->data)(); - return; - } - - if(linked_list_size(items) == 0) { - linked_list_add(items, &sd); - linked_list_add(items, &ctr_nand); - linked_list_add(items, &twl_nand); - linked_list_add(items, &twl_photo); - linked_list_add(items, &twl_sound); - linked_list_add(items, &dump_nand); - linked_list_add(items, &titles); - linked_list_add(items, &pending_titles); - linked_list_add(items, &tickets); - linked_list_add(items, &ext_save_data); - linked_list_add(items, &system_save_data); - linked_list_add(items, &remote_install); - linked_list_add(items, &update); - } -} - -void mainmenu_open() { - resources_load(); - - list_display("主菜单", "A: 选择, START: 退出", NULL, mainmenu_update, mainmenu_draw_top); -} diff --git a/source/fbi/pendingtitles.c b/source/fbi/pendingtitles.c deleted file mode 100644 index b369f2b..0000000 --- a/source/fbi/pendingtitles.c +++ /dev/null @@ -1,141 +0,0 @@ -#include -#include - -#include <3ds.h> - -#include "resources.h" -#include "section.h" -#include "action/action.h" -#include "task/uitask.h" -#include "../core/core.h" - -static list_item delete_pending_title = {"删除未完成的应用", COLOR_TEXT, action_delete_pending_title}; -static list_item delete_all_pending_titles = {"删除所有未完成的应用", COLOR_TEXT, action_delete_all_pending_titles}; - -typedef struct { - populate_pending_titles_data populateData; - - bool populated; -} pendingtitles_data; - -typedef struct { - linked_list* items; - list_item* selected; -} pendingtitles_action_data; - -static void pendingtitles_action_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { - task_draw_pending_title_info(view, ((pendingtitles_action_data*) data)->selected->data, x1, y1, x2, y2); -} - -static void pendingtitles_action_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { - pendingtitles_action_data* actionData = (pendingtitles_action_data*) data; - - if(hidKeysDown() & KEY_B) { - ui_pop(); - list_destroy(view); - - free(data); - - return; - } - - if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { - void(*action)(linked_list*, list_item*) = (void(*)(linked_list*, list_item*)) selected->data; - - ui_pop(); - list_destroy(view); - - action(actionData->items, actionData->selected); - - free(data); - - return; - } - - if(linked_list_size(items) == 0) { - linked_list_add(items, &delete_pending_title); - linked_list_add(items, &delete_all_pending_titles); - } -} - -static void pendingtitles_action_open(linked_list* items, list_item* selected) { - pendingtitles_action_data* data = (pendingtitles_action_data*) calloc(1, sizeof(pendingtitles_action_data)); - if(data == NULL) { - error_display(NULL, NULL, "无法分配未完成的应用操作的数据."); - - return; - } - - data->items = items; - data->selected = selected; - - list_display("未完成的应用操作", "A: 选择, B: 返回", data, pendingtitles_action_update, pendingtitles_action_draw_top); -} - -static void pendingtitles_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { - if(selected != NULL && selected->data != NULL) { - task_draw_pending_title_info(view, selected->data, x1, y1, x2, y2); - } -} - -static void pendingtitles_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { - pendingtitles_data* listData = (pendingtitles_data*) data; - - if(hidKeysDown() & KEY_B) { - if(!listData->populateData.finished) { - svcSignalEvent(listData->populateData.cancelEvent); - while(!listData->populateData.finished) { - svcSleepThread(1000000); - } - } - - ui_pop(); - - task_clear_pending_titles(items); - list_destroy(view); - - free(listData); - return; - } - - if(!listData->populated || (hidKeysDown() & KEY_X)) { - if(!listData->populateData.finished) { - svcSignalEvent(listData->populateData.cancelEvent); - while(!listData->populateData.finished) { - svcSleepThread(1000000); - } - } - - listData->populateData.items = items; - Result res = task_populate_pending_titles(&listData->populateData); - if(R_FAILED(res)) { - error_display_res(NULL, NULL, res, "无法启动未完成的应用列表填充."); - } - - listData->populated = true; - } - - if(listData->populateData.finished && R_FAILED(listData->populateData.result)) { - error_display_res(NULL, NULL, listData->populateData.result, "无法填充未完成的应用列表."); - - listData->populateData.result = 0; - } - - if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { - pendingtitles_action_open(items, selected); - return; - } -} - -void pendingtitles_open() { - pendingtitles_data* data = (pendingtitles_data*) calloc(1, sizeof(pendingtitles_data)); - if(data == NULL) { - error_display(NULL, NULL, "无法分配未完成的应用的数据."); - - return; - } - - data->populateData.finished = true; - - list_display("未完成的应用", "A: 选择, B: 返回, X: 刷新", data, pendingtitles_update, pendingtitles_draw_top); -} diff --git a/source/fbi/remoteinstall.c b/source/fbi/remoteinstall.c deleted file mode 100644 index 62ccc65..0000000 --- a/source/fbi/remoteinstall.c +++ /dev/null @@ -1,500 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include <3ds.h> - -#include "resources.h" -#include "section.h" -#include "action/action.h" -#include "task/uitask.h" -#include "../core/core.h" -#include "../libs/quirc/quirc_internal.h" - -static bool remoteinstall_get_last_urls(char* out, size_t size) { - if(out == NULL || size == 0) { - return false; - } - - Handle file = 0; - if(R_FAILED(FSUSER_OpenFileDirectly(&file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, "/fbi/lasturls"), FS_OPEN_READ, 0))) { - return false; - } - - u32 bytesRead = 0; - FSFILE_Read(file, &bytesRead, 0, out, size - 1); - out[bytesRead] = '\0'; - - FSFILE_Close(file); - - return bytesRead != 0; -} - -static Result remoteinstall_set_last_urls(const char* urls) { - Result res = 0; - - FS_Archive sdmcArchive = 0; - if(R_SUCCEEDED(res = FSUSER_OpenArchive(&sdmcArchive, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")))) { - FS_Path path = fsMakePath(PATH_ASCII, "/fbi/lasturls"); - - Handle file = 0; - if(R_SUCCEEDED(FSUSER_OpenFile(&file, sdmcArchive, path, FS_OPEN_READ, 0))) { - FSFILE_Close(file); - - res = FSUSER_DeleteFile(sdmcArchive, path); - } - - if(urls != NULL && strlen(urls) != 0 - && R_SUCCEEDED(res) - && R_SUCCEEDED(res = fs_ensure_dir(sdmcArchive, "/fbi/")) - && R_SUCCEEDED(res = FSUSER_OpenFile(&file, sdmcArchive, path, FS_OPEN_WRITE | FS_OPEN_CREATE, 0))) { - u32 bytesWritten = 0; - res = FSFILE_Write(file, &bytesWritten, 0, urls, strlen(urls), FS_WRITE_FLUSH | FS_WRITE_UPDATE_TIME); - - Result closeRes = FSFILE_Close(file); - if(R_SUCCEEDED(res)) { - res = closeRes; - } - } - - Result closeRes = FSUSER_CloseArchive(sdmcArchive); - if(R_SUCCEEDED(res)) { - res = closeRes; - } - } - - return res; -} - -typedef struct { - int serverSocket; - int clientSocket; -} remoteinstall_network_data; - -static int remoteinstall_network_recvwait(int sockfd, void* buf, size_t len, int flags) { - errno = 0; - - int ret = 0; - size_t read = 0; - while((((ret = recv(sockfd, buf + read, len - read, flags)) > 0 && (read += ret) < len) || errno == EAGAIN) && !(hidKeysDown() & KEY_B)) { - errno = 0; - } - - return ret < 0 ? ret : (int) read; -} - -static int remoteinstall_network_sendwait(int sockfd, void* buf, size_t len, int flags) { - errno = 0; - - int ret = 0; - size_t written = 0; - while((((ret = send(sockfd, buf + written, len - written, flags)) > 0 && (written += ret) < len) || errno == EAGAIN) && !(hidKeysDown() & KEY_B)) { - errno = 0; - } - - return ret < 0 ? ret : (int) written; -} - -static void remoteinstall_network_close_client(void* data) { - remoteinstall_network_data* networkData = (remoteinstall_network_data*) data; - - if(networkData->clientSocket != 0) { - u8 ack = 0; - remoteinstall_network_sendwait(networkData->clientSocket, &ack, sizeof(ack), 0); - - close(networkData->clientSocket); - networkData->clientSocket = 0; - } -} - - -static void remoteinstall_network_free_data(remoteinstall_network_data* data) { - remoteinstall_network_close_client(data); - - if(data->serverSocket != 0) { - close(data->serverSocket); - data->serverSocket = 0; - } - - free(data); -} - -static void remoteinstall_network_update(ui_view* view, void* data, float* progress, char* text) { - remoteinstall_network_data* networkData = (remoteinstall_network_data*) data; - - if(hidKeysDown() & KEY_B) { - ui_pop(); - info_destroy(view); - - remoteinstall_network_free_data(networkData); - - return; - } - - struct sockaddr_in client; - socklen_t clientLen = sizeof(client); - - int sock = accept(networkData->serverSocket, (struct sockaddr*) &client, &clientLen); - if(sock >= 0) { - networkData->clientSocket = sock; - - u32 size = 0; - if(remoteinstall_network_recvwait(networkData->clientSocket, &size, sizeof(size), 0) != sizeof(size)) { - error_display_errno(NULL, NULL, errno, "无法读取载荷大小."); - - remoteinstall_network_close_client(data); - return; - } - - size = ntohl(size); - if(size >= DOWNLOAD_URL_MAX * INSTALL_URLS_MAX) { - error_display(NULL, NULL, "载荷过大."); - - remoteinstall_network_close_client(data); - return; - } - - char* urls = (char*) calloc(size + 1, sizeof(char)); - if(urls == NULL) { - error_display(NULL, NULL, "无法分配链接的缓存."); - - remoteinstall_network_close_client(data); - return; - } - - if(remoteinstall_network_recvwait(networkData->clientSocket, urls, size, 0) != size) { - error_display_errno(NULL, NULL, errno, "无法读取链接."); - - free(urls); - remoteinstall_network_close_client(data); - return; - } - - remoteinstall_set_last_urls(urls); - action_install_url("从接收到的链接安装?", urls, NULL, data, NULL, remoteinstall_network_close_client, NULL); - - free(urls); - } else if(errno != EAGAIN) { - if(errno == 22 || errno == 115) { - ui_pop(); - info_destroy(view); - } - - error_display_errno(NULL, NULL, errno, "无法打开接口."); - - if(errno == 22 || errno == 115) { - remoteinstall_network_free_data(networkData); - - return; - } - } - - struct in_addr addr = {(in_addr_t) gethostid()}; - snprintf(text, PROGRESS_TEXT_MAX, "等待连接中...\nIP: %s\n端口: 5000", inet_ntoa(addr)); -} - -static void remoteinstall_receive_urls_network() { - remoteinstall_network_data* data = (remoteinstall_network_data*) calloc(1, sizeof(remoteinstall_network_data)); - if(data == NULL) { - error_display(NULL, NULL, "无法分配网络安装的数据."); - - return; - } - - int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); - if(sock < 0) { - error_display_errno(NULL, NULL, errno, "无法打开服务接口."); - - remoteinstall_network_free_data(data); - return; - } - - data->serverSocket = sock; - - struct sockaddr_in server; - server.sin_family = AF_INET; - server.sin_port = htons(5000); - server.sin_addr.s_addr = (in_addr_t) gethostid(); - - if(bind(data->serverSocket, (struct sockaddr*) &server, sizeof(server)) < 0) { - error_display_errno(NULL, NULL, errno, "无法绑定服务接口."); - - remoteinstall_network_free_data(data); - return; - } - - fcntl(data->serverSocket, F_SETFL, fcntl(data->serverSocket, F_GETFL, 0) | O_NONBLOCK); - - if(listen(data->serverSocket, 5) < 0) { - error_display_errno(NULL, NULL, errno, "无法监听服务接口."); - - remoteinstall_network_free_data(data); - return; - } - - info_display("接收链接", "B: 返回", false, data, remoteinstall_network_update, NULL); -} - -#define QR_IMAGE_WIDTH 400 -#define QR_IMAGE_HEIGHT 240 - -typedef struct { - struct quirc* qrContext; - u32 tex; - - bool capturing; - capture_cam_data captureInfo; -} remoteinstall_qr_data; - -static void remoteinstall_qr_stop_capture(remoteinstall_qr_data* data) { - if(!data->captureInfo.finished) { - svcSignalEvent(data->captureInfo.cancelEvent); - while(!data->captureInfo.finished) { - svcSleepThread(1000000); - } - } - - data->capturing = false; - - if(data->captureInfo.buffer != NULL) { - memset(data->captureInfo.buffer, 0, QR_IMAGE_WIDTH * QR_IMAGE_HEIGHT * sizeof(u16)); - } -} - -static void remoteinstall_qr_free_data(remoteinstall_qr_data* data) { - remoteinstall_qr_stop_capture(data); - - if(data->captureInfo.buffer != NULL) { - free(data->captureInfo.buffer); - data->captureInfo.buffer = NULL; - } - - if(data->tex != 0) { - screen_unload_texture(data->tex); - data->tex = 0; - } - - if(data->qrContext != NULL) { - quirc_destroy(data->qrContext); - data->qrContext = NULL; - } - - free(data); -} - -static void remoteinstall_qr_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - remoteinstall_qr_data* installData = (remoteinstall_qr_data*) data; - - if(installData->tex != 0) { - svcWaitSynchronization(installData->captureInfo.mutex, U64_MAX); - screen_load_texture_untiled(installData->tex, installData->captureInfo.buffer, QR_IMAGE_WIDTH * QR_IMAGE_HEIGHT * sizeof(u16), QR_IMAGE_WIDTH, QR_IMAGE_HEIGHT, GPU_RGB565, false); - svcReleaseMutex(installData->captureInfo.mutex); - - screen_draw_texture(installData->tex, 0, 0, QR_IMAGE_WIDTH, QR_IMAGE_HEIGHT); - } -} - -static void remoteinstall_qr_update(ui_view* view, void* data, float* progress, char* text) { - remoteinstall_qr_data* installData = (remoteinstall_qr_data*) data; - - if(hidKeysDown() & KEY_B) { - ui_pop(); - info_destroy(view); - - remoteinstall_qr_free_data(installData); - - return; - } - - if(hidKeysDown() & KEY_X) { - remoteinstall_qr_stop_capture(installData); - - installData->captureInfo.camera = installData->captureInfo.camera == CAMERA_OUTER ? CAMERA_INNER : CAMERA_OUTER; - - return; - } - - if(!installData->capturing) { - Result capRes = task_capture_cam(&installData->captureInfo); - if(R_FAILED(capRes)) { - ui_pop(); - info_destroy(view); - - error_display_res(NULL, NULL, capRes, "无法打开相机捕获."); - - remoteinstall_qr_free_data(installData); - return; - } else { - installData->capturing = true; - } - } - - if(installData->captureInfo.finished) { - ui_pop(); - info_destroy(view); - - if(R_FAILED(installData->captureInfo.result)) { - error_display_res(NULL, NULL, installData->captureInfo.result, "捕获相机画面时出错."); - } - - remoteinstall_qr_free_data(installData); - - return; - } - - int w = 0; - int h = 0; - uint8_t* qrBuf = quirc_begin(installData->qrContext, &w, &h); - - svcWaitSynchronization(installData->captureInfo.mutex, U64_MAX); - - for(int x = 0; x < w; x++) { - for(int y = 0; y < h; y++) { - u16 px = installData->captureInfo.buffer[y * QR_IMAGE_WIDTH + x]; - qrBuf[y * w + x] = (u8) (((((px >> 11) & 0x1F) << 3) + (((px >> 5) & 0x3F) << 2) + ((px & 0x1F) << 3)) / 3); - } - } - - svcReleaseMutex(installData->captureInfo.mutex); - - quirc_end(installData->qrContext); - - int qrCount = quirc_count(installData->qrContext); - for(int i = 0; i < qrCount; i++) { - struct quirc_code qrCode; - quirc_extract(installData->qrContext, i, &qrCode); - - struct quirc_data qrData; - quirc_decode_error_t err = quirc_decode(&qrCode, &qrData); - - if(err == 0) { - remoteinstall_qr_stop_capture(installData); - - remoteinstall_set_last_urls((const char*) qrData.payload); - - action_install_url("从扫描到的二维码安装?", (const char*) qrData.payload, NULL, NULL, NULL, NULL, NULL); - return; - } - } - - snprintf(text, PROGRESS_TEXT_MAX, "等待扫描二维码中..."); -} - -static void remoteinstall_scan_qr_code() { - remoteinstall_qr_data* data = (remoteinstall_qr_data*) calloc(1, sizeof(remoteinstall_qr_data)); - if(data == NULL) { - error_display(NULL, NULL, "无法分配二维码安装的数据."); - - return; - } - - data->tex = 0; - - data->capturing = false; - - data->captureInfo.width = QR_IMAGE_WIDTH; - data->captureInfo.height = QR_IMAGE_HEIGHT; - - data->captureInfo.camera = CAMERA_OUTER; - - data->captureInfo.finished = true; - - data->qrContext = quirc_new(); - if(data->qrContext == NULL) { - error_display(NULL, NULL, "无法创建二维码框."); - - remoteinstall_qr_free_data(data); - return; - } - - if(quirc_resize(data->qrContext, QR_IMAGE_WIDTH, QR_IMAGE_HEIGHT) != 0) { - error_display(NULL, NULL, "无法调整二维码框大小."); - - remoteinstall_qr_free_data(data); - return; - } - - data->captureInfo.buffer = (u16*) calloc(1, QR_IMAGE_WIDTH * QR_IMAGE_HEIGHT * sizeof(u16)); - if(data->captureInfo.buffer == NULL) { - error_display(NULL, NULL, "无法创建图像缓存."); - - remoteinstall_qr_free_data(data); - return; - } - - data->tex = screen_allocate_free_texture(); - - info_display("二维码安装", "B: 返回, X: 切换相机", false, data, remoteinstall_qr_update, remoteinstall_qr_draw_top); -} - -static void remoteinstall_manually_enter_urls_onresponse(ui_view* view, void* data, SwkbdButton button, const char* response) { - if(button == SWKBD_BUTTON_CONFIRM) { - remoteinstall_set_last_urls(response); - - action_install_url("从输入的链接安装?", response, NULL, NULL, NULL, NULL, NULL); - } -} - -static void remoteinstall_manually_enter_urls() { - kbd_display("输入链接", "", SWKBD_TYPE_NORMAL, SWKBD_MULTILINE, SWKBD_NOTEMPTY_NOTBLANK, DOWNLOAD_URL_MAX * INSTALL_URLS_MAX, NULL, remoteinstall_manually_enter_urls_onresponse); -} - -static void remoteinstall_repeat_last_request() { - char* textBuf = (char*) calloc(1, DOWNLOAD_URL_MAX * INSTALL_URLS_MAX); - if(textBuf != NULL) { - if(remoteinstall_get_last_urls(textBuf, DOWNLOAD_URL_MAX * INSTALL_URLS_MAX)) { - action_install_url("从上次请求的链接安装?", textBuf, NULL, NULL, NULL, NULL, NULL); - } else { - prompt_display_notify("失败", "没有找到以前请求的链接.", COLOR_TEXT, NULL, NULL, NULL); - } - - free(textBuf); - } else { - error_display_res(NULL, NULL, R_APP_OUT_OF_MEMORY, "无法分配链接文本的缓存."); - } -} - -static void remoteinstall_forget_last_request() { - Result forgetRes = remoteinstall_set_last_urls(NULL); - if(R_SUCCEEDED(forgetRes)) { - prompt_display_notify("成功", "已清除.", COLOR_TEXT, NULL, NULL, NULL); - } else { - error_display_res(NULL, NULL, forgetRes, "无法清除上次请求的链接."); - } -} - -static list_item receive_urls_network = {"网络接收链接", COLOR_TEXT, remoteinstall_receive_urls_network}; -static list_item scan_qr_code = {"扫描二维码", COLOR_TEXT, remoteinstall_scan_qr_code}; -static list_item manually_enter_urls = {"手动输入链接", COLOR_TEXT, remoteinstall_manually_enter_urls}; -static list_item repeat_last_request = {"重复上次请求的链接", COLOR_TEXT, remoteinstall_repeat_last_request}; -static list_item forget_last_request = {"清除上次请求的链接", COLOR_TEXT, remoteinstall_forget_last_request}; - -static void remoteinstall_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { - if(hidKeysDown() & KEY_B) { - ui_pop(); - list_destroy(view); - - return; - } - - if(selected != NULL && (selectedTouched || hidKeysDown() & KEY_A) && selected->data != NULL) { - ((void(*)()) selected->data)(); - return; - } - - if(linked_list_size(items) == 0) { - linked_list_add(items, &receive_urls_network); - linked_list_add(items, &scan_qr_code); - linked_list_add(items, &manually_enter_urls); - linked_list_add(items, &repeat_last_request); - linked_list_add(items, &forget_last_request); - } -} - -void remoteinstall_open() { - list_display("远程安装", "A: 选择, B: 返回", NULL, remoteinstall_update, NULL); -} diff --git a/source/fbi/resources.c b/source/fbi/resources.c deleted file mode 100644 index 0dd8b98..0000000 --- a/source/fbi/resources.c +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include -#include - -#include <3ds.h> - -#include "resources.h" -#include "../core/core.h" - -static FILE* resources_open_file(const char* path) { - char realPath[FILE_PATH_MAX]; - snprintf(realPath, sizeof(realPath), "sdmc:/fbi/theme/%s", path); - - FILE* fd = fopen(realPath, "rb"); - - if(fd != NULL) { - return fd; - } else { - snprintf(realPath, sizeof(realPath), "romfs:/%s", path); - - return fopen(realPath, "rb"); - } -} - -static void resources_load_texture(u32 id, const char* name) { - FILE* fd = resources_open_file(name); - if(fd == NULL) { - error_panic("无法打开纹理 \"%s\": %s\n", name, strerror(errno)); - return; - } - - screen_load_texture_file(id, fd, true); - - fclose(fd); -} - -void resources_load() { - FILE* fd = resources_open_file("textcolor.cfg"); - if(fd == NULL) { - error_panic("无法打开文本颜色配置: %s\n", strerror(errno)); - return; - } - - char line[128]; - while(fgets(line, sizeof(line), fd) != NULL) { - char key[64]; - u32 color = 0; - - sscanf(line, "%63[^=]=%lx", key, &color); - - if(strcasecmp(key, "text") == 0) { - screen_set_color(COLOR_TEXT, color); - } else if(strcasecmp(key, "nand") == 0) { - screen_set_color(COLOR_NAND, color); - } else if(strcasecmp(key, "sd") == 0) { - screen_set_color(COLOR_SD, color); - } else if(strcasecmp(key, "gamecard") == 0) { - screen_set_color(COLOR_GAME_CARD, color); - } else if(strcasecmp(key, "dstitle") == 0) { - screen_set_color(COLOR_DS_TITLE, color); - } else if(strcasecmp(key, "file") == 0) { - screen_set_color(COLOR_FILE, color); - } else if(strcasecmp(key, "directory") == 0) { - screen_set_color(COLOR_DIRECTORY, color); - } else if(strcasecmp(key, "enabled") == 0) { - screen_set_color(COLOR_ENABLED, color); - } else if(strcasecmp(key, "disabled") == 0) { - screen_set_color(COLOR_DISABLED, color); - } else if(strcasecmp(key, "ticketinuse") == 0) { - screen_set_color(COLOR_TICKET_IN_USE, color); - } else if(strcasecmp(key, "ticketnotinuse") == 0) { - screen_set_color(COLOR_TICKET_NOT_IN_USE, color); - } - } - - fclose(fd); - - resources_load_texture(TEXTURE_BOTTOM_SCREEN_BG, "bottom_screen_bg.png"); - resources_load_texture(TEXTURE_BOTTOM_SCREEN_TOP_BAR, "bottom_screen_top_bar.png"); - resources_load_texture(TEXTURE_BOTTOM_SCREEN_TOP_BAR_SHADOW, "bottom_screen_top_bar_shadow.png"); - resources_load_texture(TEXTURE_BOTTOM_SCREEN_BOTTOM_BAR, "bottom_screen_bottom_bar.png"); - resources_load_texture(TEXTURE_BOTTOM_SCREEN_BOTTOM_BAR_SHADOW, "bottom_screen_bottom_bar_shadow.png"); - resources_load_texture(TEXTURE_TOP_SCREEN_BG, "top_screen_bg.png"); - resources_load_texture(TEXTURE_TOP_SCREEN_TOP_BAR, "top_screen_top_bar.png"); - resources_load_texture(TEXTURE_TOP_SCREEN_TOP_BAR_SHADOW, "top_screen_top_bar_shadow.png"); - resources_load_texture(TEXTURE_TOP_SCREEN_BOTTOM_BAR, "top_screen_bottom_bar.png"); - resources_load_texture(TEXTURE_TOP_SCREEN_BOTTOM_BAR_SHADOW, "top_screen_bottom_bar_shadow.png"); - resources_load_texture(TEXTURE_LOGO, "logo.png"); - resources_load_texture(TEXTURE_SELECTION_OVERLAY, "selection_overlay.png"); - resources_load_texture(TEXTURE_SCROLL_BAR, "scroll_bar.png"); - resources_load_texture(TEXTURE_BUTTON, "button.png"); - resources_load_texture(TEXTURE_PROGRESS_BAR_BG, "progress_bar_bg.png"); - resources_load_texture(TEXTURE_PROGRESS_BAR_CONTENT, "progress_bar_content.png"); - resources_load_texture(TEXTURE_META_INFO_BOX, "meta_info_box.png"); - resources_load_texture(TEXTURE_META_INFO_BOX_SHADOW, "meta_info_box_shadow.png"); - resources_load_texture(TEXTURE_BATTERY_CHARGING, "battery_charging.png"); - resources_load_texture(TEXTURE_BATTERY_0, "battery0.png"); - resources_load_texture(TEXTURE_BATTERY_1, "battery1.png"); - resources_load_texture(TEXTURE_BATTERY_2, "battery2.png"); - resources_load_texture(TEXTURE_BATTERY_3, "battery3.png"); - resources_load_texture(TEXTURE_BATTERY_4, "battery4.png"); - resources_load_texture(TEXTURE_BATTERY_5, "battery5.png"); - resources_load_texture(TEXTURE_WIFI_DISCONNECTED, "wifi_disconnected.png"); - resources_load_texture(TEXTURE_WIFI_0, "wifi0.png"); - resources_load_texture(TEXTURE_WIFI_1, "wifi1.png"); - resources_load_texture(TEXTURE_WIFI_2, "wifi2.png"); - resources_load_texture(TEXTURE_WIFI_3, "wifi3.png"); -} diff --git a/source/fbi/resources.h b/source/fbi/resources.h deleted file mode 100644 index 79afbfe..0000000 --- a/source/fbi/resources.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#define TEXTURE_BOTTOM_SCREEN_BG 1 -#define TEXTURE_BOTTOM_SCREEN_TOP_BAR 2 -#define TEXTURE_BOTTOM_SCREEN_TOP_BAR_SHADOW 3 -#define TEXTURE_BOTTOM_SCREEN_BOTTOM_BAR 4 -#define TEXTURE_BOTTOM_SCREEN_BOTTOM_BAR_SHADOW 5 -#define TEXTURE_TOP_SCREEN_BG 6 -#define TEXTURE_TOP_SCREEN_TOP_BAR 7 -#define TEXTURE_TOP_SCREEN_TOP_BAR_SHADOW 8 -#define TEXTURE_TOP_SCREEN_BOTTOM_BAR 9 -#define TEXTURE_TOP_SCREEN_BOTTOM_BAR_SHADOW 10 -#define TEXTURE_LOGO 11 -#define TEXTURE_SELECTION_OVERLAY 12 -#define TEXTURE_SCROLL_BAR 13 -#define TEXTURE_BUTTON 14 -#define TEXTURE_PROGRESS_BAR_BG 15 -#define TEXTURE_PROGRESS_BAR_CONTENT 16 -#define TEXTURE_META_INFO_BOX 17 -#define TEXTURE_META_INFO_BOX_SHADOW 18 -#define TEXTURE_BATTERY_CHARGING 19 -#define TEXTURE_BATTERY_0 20 -#define TEXTURE_BATTERY_1 21 -#define TEXTURE_BATTERY_2 22 -#define TEXTURE_BATTERY_3 23 -#define TEXTURE_BATTERY_4 24 -#define TEXTURE_BATTERY_5 25 -#define TEXTURE_WIFI_DISCONNECTED 26 -#define TEXTURE_WIFI_0 27 -#define TEXTURE_WIFI_1 28 -#define TEXTURE_WIFI_2 29 -#define TEXTURE_WIFI_3 30 - -#define COLOR_NAND 1 -#define COLOR_SD 2 -#define COLOR_GAME_CARD 3 -#define COLOR_DS_TITLE 4 -#define COLOR_FILE 5 -#define COLOR_DIRECTORY 6 -#define COLOR_ENABLED 7 -#define COLOR_DISABLED 8 -#define COLOR_TICKET_IN_USE 9 -#define COLOR_TICKET_NOT_IN_USE 10 - -void resources_load(); \ No newline at end of file diff --git a/source/fbi/section.h b/source/fbi/section.h deleted file mode 100644 index cdf8e48..0000000 --- a/source/fbi/section.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -void mainmenu_open(); - -void dumpnand_open(); -void extsavedata_open(); -void files_open(FS_ArchiveID archiveId, FS_Path archivePath); -void files_open_sd(); -void files_open_ctr_nand(); -void files_open_twl_nand(); -void files_open_twl_photo(); -void files_open_twl_sound(); -void pendingtitles_open(); -void remoteinstall_open(); -void systemsavedata_open(); -void tickets_open(); -void titles_open(); -void update_open(); \ No newline at end of file diff --git a/source/fbi/systemsavedata.c b/source/fbi/systemsavedata.c deleted file mode 100644 index 72a0381..0000000 --- a/source/fbi/systemsavedata.c +++ /dev/null @@ -1,141 +0,0 @@ -#include -#include - -#include <3ds.h> - -#include "resources.h" -#include "section.h" -#include "action/action.h" -#include "task/uitask.h" -#include "../core/core.h" - -static list_item browse_save_data = {"浏览数据", COLOR_TEXT, action_browse_system_save_data}; -static list_item delete_save_data = {"删除数据", COLOR_TEXT, action_delete_system_save_data}; - -typedef struct { - populate_system_save_data_data populateData; - - bool populated; -} systemsavedata_data; - -typedef struct { - linked_list* items; - list_item* selected; -} systemsavedata_action_data; - -static void systemsavedata_action_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { - task_draw_system_save_data_info(view, ((systemsavedata_action_data*) data)->selected->data, x1, y1, x2, y2); -} - -static void systemsavedata_action_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { - systemsavedata_action_data* actionData = (systemsavedata_action_data*) data; - - if(hidKeysDown() & KEY_B) { - ui_pop(); - list_destroy(view); - - free(data); - - return; - } - - if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { - void(*action)(linked_list*, list_item*) = (void(*)(linked_list*, list_item*)) selected->data; - - ui_pop(); - list_destroy(view); - - action(actionData->items, actionData->selected); - - free(data); - - return; - } - - if(linked_list_size(items) == 0) { - linked_list_add(items, &browse_save_data); - linked_list_add(items, &delete_save_data); - } -} - -static void systemsavedata_action_open(linked_list* items, list_item* selected) { - systemsavedata_action_data* data = (systemsavedata_action_data*) calloc(1, sizeof(systemsavedata_action_data)); - if(data == NULL) { - error_display(NULL, NULL, "无法分配系统数据操作的数据."); - - return; - } - - data->items = items; - data->selected = selected; - - list_display("系统数据操作", "A: 选择, B: 返回", data, systemsavedata_action_update, systemsavedata_action_draw_top); -} - -static void systemsavedata_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { - if(selected != NULL && selected->data != NULL) { - task_draw_system_save_data_info(view, selected->data, x1, y1, x2, y2); - } -} - -static void systemsavedata_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { - systemsavedata_data* listData = (systemsavedata_data*) data; - - if(hidKeysDown() & KEY_B) { - if(!listData->populateData.finished) { - svcSignalEvent(listData->populateData.cancelEvent); - while(!listData->populateData.finished) { - svcSleepThread(1000000); - } - } - - ui_pop(); - - task_clear_system_save_data(items); - list_destroy(view); - - free(listData); - return; - } - - if(!listData->populated || (hidKeysDown() & KEY_X)) { - if(!listData->populateData.finished) { - svcSignalEvent(listData->populateData.cancelEvent); - while(!listData->populateData.finished) { - svcSleepThread(1000000); - } - } - - listData->populateData.items = items; - Result res = task_populate_system_save_data(&listData->populateData); - if(R_FAILED(res)) { - error_display_res(NULL, NULL, res, "无法启动系统数据列表填充."); - } - - listData->populated = true; - } - - if(listData->populateData.finished && R_FAILED(listData->populateData.result)) { - error_display_res(NULL, NULL, listData->populateData.result, "无法填充系统数据列表."); - - listData->populateData.result = 0; - } - - if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { - systemsavedata_action_open(items, selected); - return; - } -} - -void systemsavedata_open() { - systemsavedata_data* data = (systemsavedata_data*) calloc(1, sizeof(systemsavedata_data)); - if(data == NULL) { - error_display(NULL, NULL, "无法分配系统数据的数据."); - - return; - } - - data->populateData.finished = true; - - list_display("系统数据", "A: 选择, B: 返回, X: 刷新", data, systemsavedata_update, systemsavedata_draw_top); -} diff --git a/source/fbi/task/listextsavedata.c b/source/fbi/task/listextsavedata.c deleted file mode 100644 index df9aff0..0000000 --- a/source/fbi/task/listextsavedata.c +++ /dev/null @@ -1,174 +0,0 @@ -#include -#include -#include -#include - -#include <3ds.h> - -#include "uitask.h" -#include "listextsavedata.h" -#include "../resources.h" -#include "../../core/core.h" - -#define MAX_EXT_SAVE_DATA 512 - -static int task_populate_ext_save_data_compare_ids(const void* e1, const void* e2) { - u64 id1 = *(u64*) e1; - u64 id2 = *(u64*) e2; - - return id1 > id2 ? 1 : id1 < id2 ? -1 : 0; -} - -static Result task_populate_ext_save_data_from(populate_ext_save_data_data* data, FS_MediaType mediaType) { - Result res = 0; - - u32 extSaveDataCount = 0; - u64 extSaveDataIds[MAX_EXT_SAVE_DATA]; - if(R_SUCCEEDED(res = FSUSER_EnumerateExtSaveData(&extSaveDataCount, MAX_EXT_SAVE_DATA, mediaType, 8, mediaType == MEDIATYPE_NAND, (u8*) extSaveDataIds))) { - qsort(extSaveDataIds, extSaveDataCount, sizeof(u64), task_populate_ext_save_data_compare_ids); - - for(u32 i = 0; i < extSaveDataCount && R_SUCCEEDED(res); i++) { - svcWaitSynchronization(task_get_pause_event(), U64_MAX); - if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { - break; - } - - if(data->filter == NULL || data->filter(data->userData, extSaveDataIds[i], mediaType)) { - list_item* item = (list_item*) calloc(1, sizeof(list_item)); - if(item != NULL) { - ext_save_data_info* extSaveDataInfo = (ext_save_data_info*) calloc(1, sizeof(ext_save_data_info)); - if(extSaveDataInfo != NULL) { - extSaveDataInfo->mediaType = mediaType; - extSaveDataInfo->extSaveDataId = extSaveDataIds[i]; - extSaveDataInfo->shared = mediaType == MEDIATYPE_NAND; - extSaveDataInfo->hasMeta = false; - - FS_ExtSaveDataInfo info = {.mediaType = mediaType, .saveId = extSaveDataIds[i]}; - - SMDH* smdh = (SMDH*) calloc(1, sizeof(SMDH)); - if(smdh != NULL) { - u32 smdhBytesRead = 0; - if(R_SUCCEEDED(FSUSER_ReadExtSaveDataIcon(&smdhBytesRead, info, sizeof(SMDH), (u8*) smdh)) && smdhBytesRead == sizeof(SMDH)) { - if(smdh->magic[0] == 'S' && smdh->magic[1] == 'M' && smdh->magic[2] == 'D' && smdh->magic[3] == 'H') { - SMDH_title* smdhTitle = smdh_select_title(smdh); - - utf16_to_utf8((uint8_t*) item->name, smdhTitle->shortDescription, LIST_ITEM_NAME_MAX - 1); - - extSaveDataInfo->hasMeta = true; - utf16_to_utf8((uint8_t*) extSaveDataInfo->meta.shortDescription, smdhTitle->shortDescription, sizeof(extSaveDataInfo->meta.shortDescription) - 1); - utf16_to_utf8((uint8_t*) extSaveDataInfo->meta.longDescription, smdhTitle->longDescription, sizeof(extSaveDataInfo->meta.longDescription) - 1); - utf16_to_utf8((uint8_t*) extSaveDataInfo->meta.publisher, smdhTitle->publisher, sizeof(extSaveDataInfo->meta.publisher) - 1); - extSaveDataInfo->meta.region = smdh->region; - extSaveDataInfo->meta.texture = screen_allocate_free_texture(); - screen_load_texture_tiled(extSaveDataInfo->meta.texture, smdh->largeIcon, sizeof(smdh->largeIcon), 48, 48, GPU_RGB565, false); - } - } - - free(smdh); - } - - if(string_is_empty(item->name)) { - snprintf(item->name, LIST_ITEM_NAME_MAX, "%016llX", extSaveDataIds[i]); - } - - if(mediaType == MEDIATYPE_NAND) { - item->color = COLOR_NAND; - } else if(mediaType == MEDIATYPE_SD) { - item->color = COLOR_SD; - } - - item->data = extSaveDataInfo; - - linked_list_add_sorted(data->items, item, data->userData, data->compare); - } else { - free(item); - - res = R_APP_OUT_OF_MEMORY; - } - } else { - res = R_APP_OUT_OF_MEMORY; - } - } - } - } - - return res; -} - -static void task_populate_ext_save_data_thread(void* arg) { - populate_ext_save_data_data* data = (populate_ext_save_data_data*) arg; - - Result res = 0; - - if(R_SUCCEEDED(res = task_populate_ext_save_data_from(data, MEDIATYPE_SD))) { - res = task_populate_ext_save_data_from(data, MEDIATYPE_NAND); - } - - svcCloseHandle(data->cancelEvent); - - data->result = res; - data->finished = true; -} - -void task_free_ext_save_data(list_item* item) { - if(item == NULL) { - return; - } - - if(item->data != NULL) { - ext_save_data_info* extSaveDataInfo = (ext_save_data_info*) item->data; - if(extSaveDataInfo->hasMeta) { - screen_unload_texture(extSaveDataInfo->meta.texture); - } - - free(item->data); - } - - free(item); -} - -void task_clear_ext_save_data(linked_list* items) { - if(items == NULL) { - return; - } - - linked_list_iter iter; - linked_list_iterate(items, &iter); - - while(linked_list_iter_has_next(&iter)) { - list_item* item = (list_item*) linked_list_iter_next(&iter); - - linked_list_iter_remove(&iter); - task_free_ext_save_data(item); - } -} - -Result task_populate_ext_save_data(populate_ext_save_data_data* data) { - if(data == NULL || data->items == NULL) { - return R_APP_INVALID_ARGUMENT; - } - - task_clear_ext_save_data(data->items); - - data->finished = false; - data->result = 0; - data->cancelEvent = 0; - - Result res = 0; - if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, RESET_STICKY))) { - if(threadCreate(task_populate_ext_save_data_thread, data, 0x10000, 0x19, 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; - } - } - - return res; -} \ No newline at end of file diff --git a/source/fbi/task/listextsavedata.h b/source/fbi/task/listextsavedata.h deleted file mode 100644 index 6f6005a..0000000 --- a/source/fbi/task/listextsavedata.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -typedef struct linked_list_s linked_list; -typedef struct list_item_s list_item; - -typedef struct ext_save_data_info_s { - FS_MediaType mediaType; - u64 extSaveDataId; - bool shared; - bool hasMeta; - meta_info meta; -} ext_save_data_info; - -typedef struct populate_ext_save_data_data_s { - linked_list* items; - - void* userData; - bool (*filter)(void* data, u64 extSaveDataId, FS_MediaType mediaType); - int (*compare)(void* data, const void* p1, const void* p2); - - volatile bool finished; - Result result; - Handle cancelEvent; -} populate_ext_save_data_data; - -void task_free_ext_save_data(list_item* item); -void task_clear_ext_save_data(linked_list* items); -Result task_populate_ext_save_data(populate_ext_save_data_data* data); \ No newline at end of file diff --git a/source/fbi/task/listfiles.c b/source/fbi/task/listfiles.c deleted file mode 100644 index 0d5cc02..0000000 --- a/source/fbi/task/listfiles.c +++ /dev/null @@ -1,366 +0,0 @@ -#include -#include -#include -#include - -#include <3ds.h> - -#include "uitask.h" -#include "listfiles.h" -#include "../resources.h" -#include "../../core/core.h" - -#define MAX_FILES 1024 - -int task_compare_files(void* userData, const void* p1, const void* p2) { - list_item* info1 = (list_item*) p1; - list_item* info2 = (list_item*) p2; - - bool info1Base = strncmp(info1->name, "<当前文件夹>", LIST_ITEM_NAME_MAX) == 0 || strncmp(info1->name, "<当前文件>", LIST_ITEM_NAME_MAX) == 0; - bool info2Base = strncmp(info2->name, "<当前文件夹>", LIST_ITEM_NAME_MAX) == 0 || strncmp(info2->name, "<当前文件>", LIST_ITEM_NAME_MAX) == 0; - - if(info1Base && !info2Base) { - return -1; - } else if(!info1Base && info2Base) { - return 1; - } else { - file_info* f1 = (file_info*) info1->data; - file_info* f2 = (file_info*) info2->data; - - if((f1->attributes & FS_ATTRIBUTE_DIRECTORY) && !(f2->attributes & FS_ATTRIBUTE_DIRECTORY)) { - return -1; - } else if(!(f1->attributes & FS_ATTRIBUTE_DIRECTORY) && (f2->attributes & FS_ATTRIBUTE_DIRECTORY)) { - return 1; - } else { - return strncasecmp(f1->name, f2->name, FILE_NAME_MAX); - } - } -} - -static void task_populate_files_retrieve_meta(file_info* fileInfo) { - FS_Path* fileFsPath = fs_make_path_utf8(fileInfo->path); - if(fileFsPath != NULL) { - Handle fileHandle; - if(R_SUCCEEDED(FSUSER_OpenFile(&fileHandle, fileInfo->archive, *fileFsPath, FS_OPEN_READ, 0))) { - if(fileInfo->attributes == 0 && R_FAILED(FSFILE_GetAttributes(fileHandle, &fileInfo->attributes))) { - fileInfo->attributes = 0; - } - - FSFILE_GetSize(fileHandle, &fileInfo->size); - - if(fileInfo->isCia) { - AM_TitleEntry titleEntry; - if(R_SUCCEEDED(AM_GetCiaFileInfo(MEDIATYPE_SD, &titleEntry, fileHandle))) { - fileInfo->ciaInfo.titleId = titleEntry.titleID; - fileInfo->ciaInfo.version = titleEntry.version; - fileInfo->ciaInfo.installedSize = titleEntry.size; - fileInfo->ciaInfo.hasMeta = false; - - if(fs_get_title_destination(titleEntry.titleID) != MEDIATYPE_SD && R_SUCCEEDED(AM_GetCiaFileInfo(MEDIATYPE_NAND, &titleEntry, fileHandle))) { - fileInfo->ciaInfo.installedSize = titleEntry.size; - } - - SMDH* smdh = (SMDH*) calloc(1, sizeof(SMDH)); - if(smdh != NULL) { - if(R_SUCCEEDED(cia_file_get_smdh(smdh, fileHandle))) { - if(smdh->magic[0] == 'S' && smdh->magic[1] == 'M' && smdh->magic[2] == 'D' && smdh->magic[3] == 'H') { - SMDH_title* smdhTitle = smdh_select_title(smdh); - - fileInfo->ciaInfo.hasMeta = true; - utf16_to_utf8((uint8_t*) fileInfo->ciaInfo.meta.shortDescription, smdhTitle->shortDescription, sizeof(fileInfo->ciaInfo.meta.shortDescription) - 1); - utf16_to_utf8((uint8_t*) fileInfo->ciaInfo.meta.longDescription, smdhTitle->longDescription, sizeof(fileInfo->ciaInfo.meta.longDescription) - 1); - utf16_to_utf8((uint8_t*) fileInfo->ciaInfo.meta.publisher, smdhTitle->publisher, sizeof(fileInfo->ciaInfo.meta.publisher) - 1); - fileInfo->ciaInfo.meta.region = smdh->region; - fileInfo->ciaInfo.meta.texture = screen_allocate_free_texture(); - screen_load_texture_tiled(fileInfo->ciaInfo.meta.texture, smdh->largeIcon, sizeof(smdh->largeIcon), 48, 48, GPU_RGB565, false); - } - } - - free(smdh); - } - - fileInfo->ciaInfo.loaded = true; - } else { - fileInfo->isCia = false; - } - } else if(fileInfo->isTicket) { - u32 bytesRead = 0; - - u8 sigType = 0; - if(R_SUCCEEDED(FSFILE_Read(fileHandle, &bytesRead, 3, &sigType, sizeof(sigType))) && bytesRead == sizeof(sigType) && sigType <= 5) { - static u32 dataOffsets[6] = {0x240, 0x140, 0x80, 0x240, 0x140, 0x80}; - static u32 titleIdOffset = 0x9C; - - u64 titleId = 0; - if(R_SUCCEEDED(FSFILE_Read(fileHandle, &bytesRead, dataOffsets[sigType] + titleIdOffset, &titleId, sizeof(titleId))) && bytesRead == sizeof(titleId)) { - fileInfo->ticketInfo.loaded = true; - fileInfo->ticketInfo.titleId = __builtin_bswap64(titleId); - fileInfo->ticketInfo.inUse = false; - } else { - fileInfo->isTicket = false; - } - } else { - fileInfo->isTicket = false; - } - } - - FSFILE_Close(fileHandle); - } - - fs_free_path_utf8(fileFsPath); - } -} - -Result task_create_file_item(list_item** out, FS_Archive archive, const char* path, u32 attributes, bool meta) { - Result res = 0; - - list_item* item = (list_item*) calloc(1, sizeof(list_item)); - if(item != NULL) { - file_info* fileInfo = (file_info*) calloc(1, sizeof(file_info)); - if(fileInfo != NULL) { - fileInfo->archive = archive; - string_get_path_file(fileInfo->name, path, FILE_NAME_MAX); - fileInfo->attributes = attributes; - - fileInfo->size = 0; - fileInfo->isCia = false; - fileInfo->isTicket = false; - - if((attributes & FS_ATTRIBUTE_DIRECTORY) || fs_is_dir(archive, path)) { - item->color = COLOR_DIRECTORY; - - size_t len = strlen(path); - if(len == 0 || path[len - 1] != '/') { - snprintf(fileInfo->path, FILE_PATH_MAX, "%s/", path); - } else { - string_copy(fileInfo->path, path, FILE_PATH_MAX); - } - - if(attributes == 0) { - fileInfo->attributes = FS_ATTRIBUTE_DIRECTORY; - } - } else { - item->color = COLOR_FILE; - - string_copy(fileInfo->path, path, FILE_PATH_MAX); - - if(fs_filter_cias(NULL, fileInfo->path, fileInfo->attributes)) { - fileInfo->isCia = true; - } else if(fs_filter_tickets(NULL, fileInfo->path, fileInfo->attributes)) { - fileInfo->isTicket = true; - } - - if(meta) { - task_populate_files_retrieve_meta(fileInfo); - } - } - - string_copy(item->name, fileInfo->name, LIST_ITEM_NAME_MAX); - item->data = fileInfo; - - *out = item; - } else { - free(item); - - res = R_APP_OUT_OF_MEMORY; - } - } else { - res = R_APP_OUT_OF_MEMORY; - } - - return res; -} - -static int task_populate_files_compare_directory_entries(const void* e1, const void* e2) { - FS_DirectoryEntry* ent1 = (FS_DirectoryEntry*) e1; - FS_DirectoryEntry* ent2 = (FS_DirectoryEntry*) e2; - - if((ent1->attributes & FS_ATTRIBUTE_DIRECTORY) && !(ent2->attributes & FS_ATTRIBUTE_DIRECTORY)) { - return -1; - } else if(!(ent1->attributes & FS_ATTRIBUTE_DIRECTORY) && (ent2->attributes & FS_ATTRIBUTE_DIRECTORY)) { - return 1; - } else { - char entryName1[0x213] = {'\0'}; - utf16_to_utf8((uint8_t*) entryName1, ent1->name, sizeof(entryName1) - 1); - - char entryName2[0x213] = {'\0'}; - utf16_to_utf8((uint8_t*) entryName2, ent2->name, sizeof(entryName2) - 1); - - return strncasecmp(entryName1, entryName2, sizeof(entryName1)); - } -} - -static void task_populate_files_thread(void* arg) { - populate_files_data* data = (populate_files_data*) arg; - - Result res = 0; - - list_item* baseItem = NULL; - if(R_SUCCEEDED(res = task_create_file_item(&baseItem, data->archive, data->path, 0, false))) { - file_info* baseInfo = (file_info*) baseItem->data; - if(baseInfo->attributes & FS_ATTRIBUTE_DIRECTORY) { - string_copy(baseItem->name, "<当前文件夹>", LIST_ITEM_NAME_MAX); - } else { - string_copy(baseItem->name, "<当前文件>", LIST_ITEM_NAME_MAX); - } - - linked_list queue; - linked_list_init(&queue); - - linked_list_add(&queue, baseItem); - - bool quit = false; - while(!quit && R_SUCCEEDED(res) && linked_list_size(&queue) > 0) { - u32 tail = linked_list_size(&queue) - 1; - list_item* currItem = (list_item*) linked_list_get(&queue, tail); - file_info* curr = (file_info*) currItem->data; - linked_list_remove_at(&queue, tail); - - if(data->includeBase || currItem != baseItem) { - linked_list_add(data->items, currItem); - } - - if(curr->attributes & FS_ATTRIBUTE_DIRECTORY) { - FS_Path* fsPath = fs_make_path_utf8(curr->path); - if(fsPath != NULL) { - Handle dirHandle = 0; - if(R_SUCCEEDED(res = FSUSER_OpenDirectory(&dirHandle, curr->archive, *fsPath))) { - u32 entryCount = 0; - FS_DirectoryEntry* entries = (FS_DirectoryEntry*) calloc(MAX_FILES, sizeof(FS_DirectoryEntry)); - if(entries != NULL) { - if(R_SUCCEEDED(res = FSDIR_Read(dirHandle, &entryCount, MAX_FILES, entries)) && entryCount > 0) { - qsort(entries, entryCount, sizeof(FS_DirectoryEntry), task_populate_files_compare_directory_entries); - - for(u32 i = 0; i < entryCount && R_SUCCEEDED(res); i++) { - svcWaitSynchronization(task_get_pause_event(), U64_MAX); - if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { - quit = true; - break; - } - - char name[FILE_NAME_MAX] = {'\0'}; - utf16_to_utf8((uint8_t*) name, entries[i].name, FILE_NAME_MAX - 1); - - if(data->filter == NULL || data->filter(data->filterData, name, entries[i].attributes)) { - char path[FILE_PATH_MAX] = {'\0'}; - snprintf(path, FILE_PATH_MAX, "%s%s", curr->path, name); - - list_item* item = NULL; - if(R_SUCCEEDED(res = task_create_file_item(&item, curr->archive, path, entries[i].attributes, false))) { - if(data->recursive && (((file_info*) item->data)->attributes & FS_ATTRIBUTE_DIRECTORY)) { - linked_list_add(&queue, item); - } else { - linked_list_add(data->items, item); - } - } - } - } - } - - free(entries); - } else { - res = R_APP_OUT_OF_MEMORY; - } - - FSDIR_Close(dirHandle); - } - - fs_free_path_utf8(fsPath); - } else { - res = R_APP_OUT_OF_MEMORY; - } - } - } - - linked_list_destroy(&queue); - - if(!data->includeBase) { - task_free_file(baseItem); - } - } - - if(R_SUCCEEDED(res) && data->meta) { - linked_list_iter iter; - linked_list_iterate(data->items, &iter); - - while(linked_list_iter_has_next(&iter)) { - svcWaitSynchronization(task_get_pause_event(), U64_MAX); - if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { - break; - } - - list_item* item = (list_item*) linked_list_iter_next(&iter); - file_info* fileInfo = (file_info*) item->data; - - task_populate_files_retrieve_meta(fileInfo); - } - } - - svcCloseHandle(data->cancelEvent); - - data->result = res; - data->finished = true; -} - -void task_free_file(list_item* item) { - if(item == NULL) { - return; - } - - if(item->data != NULL) { - file_info* fileInfo = (file_info*) item->data; - if(fileInfo->isCia && fileInfo->ciaInfo.hasMeta) { - screen_unload_texture(fileInfo->ciaInfo.meta.texture); - } - - free(item->data); - } - - free(item); -} - -void task_clear_files(linked_list* items) { - if(items == NULL) { - return; - } - - linked_list_iter iter; - linked_list_iterate(items, &iter); - - while(linked_list_iter_has_next(&iter)) { - list_item* item = (list_item*) linked_list_iter_next(&iter); - - linked_list_iter_remove(&iter); - task_free_file(item); - } -} - -Result task_populate_files(populate_files_data* data) { - if(data == NULL || data->items == NULL) { - return R_APP_INVALID_ARGUMENT; - } - - task_clear_files(data->items); - - data->finished = false; - data->result = 0; - data->cancelEvent = 0; - - Result res = 0; - if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, RESET_STICKY))) { - if(threadCreate(task_populate_files_thread, data, 0x10000, 0x19, 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; - } - } - - return res; -} \ No newline at end of file diff --git a/source/fbi/task/listfiles.h b/source/fbi/task/listfiles.h deleted file mode 100644 index 7cd2eab..0000000 --- a/source/fbi/task/listfiles.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#ifndef FILE_NAME_MAX -#define FILE_NAME_MAX 256 -#define FILE_PATH_MAX 512 -#endif - -typedef struct linked_list_s linked_list; -typedef struct list_item_s list_item; - -typedef struct cia_info_s { - bool loaded; - - u64 titleId; - u16 version; - u64 installedSize; - bool hasMeta; - meta_info meta; -} cia_info; - -typedef struct file_info_s { - FS_Archive archive; - char name[FILE_NAME_MAX]; - char path[FILE_PATH_MAX]; - u32 attributes; - - // Files only - u64 size; - bool isCia; - cia_info ciaInfo; - bool isTicket; - ticket_info ticketInfo; -} file_info; - -typedef struct populate_files_data_s { - linked_list* items; - - FS_Archive archive; - char path[FILE_PATH_MAX]; - - bool recursive; - bool includeBase; - bool meta; - - bool (*filter)(void* data, const char* name, u32 attributes); - void* filterData; - - volatile bool finished; - Result result; - Handle cancelEvent; -} populate_files_data; - -int task_compare_files(void* userData, const void* p1, const void* p2); -void task_free_file(list_item* item); -void task_clear_files(linked_list* items); -Result task_create_file_item(list_item** out, FS_Archive archive, const char* path, u32 attributes, bool meta); -Result task_populate_files(populate_files_data* data); \ No newline at end of file diff --git a/source/fbi/task/listpendingtitles.c b/source/fbi/task/listpendingtitles.c deleted file mode 100644 index 3619cd0..0000000 --- a/source/fbi/task/listpendingtitles.c +++ /dev/null @@ -1,152 +0,0 @@ -#include -#include -#include - -#include <3ds.h> - -#include "listpendingtitles.h" -#include "../resources.h" -#include "../../core/core.h" - -static int task_populate_pending_titles_compare_ids(const void* e1, const void* e2) { - u64 id1 = *(u64*) e1; - u64 id2 = *(u64*) e2; - - return id1 > id2 ? 1 : id1 < id2 ? -1 : 0; -} - -static Result task_populate_pending_titles_from(populate_pending_titles_data* data, FS_MediaType mediaType) { - Result res = 0; - - u32 pendingTitleCount = 0; - if(R_SUCCEEDED(res = AM_GetPendingTitleCount(&pendingTitleCount, mediaType, AM_STATUS_MASK_INSTALLING | AM_STATUS_MASK_AWAITING_FINALIZATION))) { - u64* pendingTitleIds = (u64*) calloc(pendingTitleCount, sizeof(u64)); - if(pendingTitleIds != NULL) { - if(R_SUCCEEDED(res = AM_GetPendingTitleList(&pendingTitleCount, pendingTitleCount, mediaType, AM_STATUS_MASK_INSTALLING | AM_STATUS_MASK_AWAITING_FINALIZATION, pendingTitleIds))) { - qsort(pendingTitleIds, pendingTitleCount, sizeof(u64), task_populate_pending_titles_compare_ids); - - AM_PendingTitleEntry* pendingTitleInfos = (AM_PendingTitleEntry*) calloc(pendingTitleCount, sizeof(AM_PendingTitleEntry)); - if(pendingTitleInfos != NULL) { - if(R_SUCCEEDED(res = AM_GetPendingTitleInfo(pendingTitleCount, mediaType, pendingTitleIds, pendingTitleInfos))) { - for(u32 i = 0; i < pendingTitleCount && R_SUCCEEDED(res); i++) { - svcWaitSynchronization(task_get_pause_event(), U64_MAX); - if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { - break; - } - - list_item* item = (list_item*) calloc(1, sizeof(list_item)); - if(item != NULL) { - pending_title_info* pendingTitleInfo = (pending_title_info*) calloc(1, sizeof(pending_title_info)); - if(pendingTitleInfo != NULL) { - pendingTitleInfo->mediaType = mediaType; - pendingTitleInfo->titleId = pendingTitleIds[i]; - pendingTitleInfo->version = pendingTitleInfos[i].version; - - snprintf(item->name, LIST_ITEM_NAME_MAX, "%016llX", pendingTitleIds[i]); - if(mediaType == MEDIATYPE_NAND) { - item->color = COLOR_NAND; - } else if(mediaType == MEDIATYPE_SD) { - item->color = COLOR_SD; - } - - item->data = pendingTitleInfo; - - linked_list_add(data->items, item); - } else { - free(item); - - res = R_APP_OUT_OF_MEMORY; - } - } else { - res = R_APP_OUT_OF_MEMORY; - } - } - } - - free(pendingTitleInfos); - } else { - res = R_APP_OUT_OF_MEMORY; - } - } - - free(pendingTitleIds); - } else { - res = R_APP_OUT_OF_MEMORY; - } - } - - return res; -} - -static void task_populate_pending_titles_thread(void* arg) { - populate_pending_titles_data* data = (populate_pending_titles_data*) arg; - - Result res = 0; - - if(R_SUCCEEDED(res = task_populate_pending_titles_from(data, MEDIATYPE_SD))) { - res = task_populate_pending_titles_from(data, MEDIATYPE_NAND); - } - - svcCloseHandle(data->cancelEvent); - - data->result = res; - data->finished = true; -} - -void task_free_pending_title(list_item* item) { - if(item == NULL) { - return; - } - - if(item->data != NULL) { - free(item->data); - } - - free(item); -} - -void task_clear_pending_titles(linked_list* items) { - if(items == NULL) { - return; - } - - linked_list_iter iter; - linked_list_iterate(items, &iter); - - while(linked_list_iter_has_next(&iter)) { - list_item* item = (list_item*) linked_list_iter_next(&iter); - - linked_list_iter_remove(&iter); - task_free_pending_title(item); - } -} - -Result task_populate_pending_titles(populate_pending_titles_data* data) { - if(data == NULL || data->items == NULL) { - return R_APP_INVALID_ARGUMENT; - } - - task_clear_pending_titles(data->items); - - data->finished = false; - data->result = 0; - data->cancelEvent = 0; - - Result res = 0; - if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, RESET_STICKY))) { - if(threadCreate(task_populate_pending_titles_thread, data, 0x10000, 0x19, 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; - } - } - - return res; -} \ No newline at end of file diff --git a/source/fbi/task/listpendingtitles.h b/source/fbi/task/listpendingtitles.h deleted file mode 100644 index 4fb36c4..0000000 --- a/source/fbi/task/listpendingtitles.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -typedef struct linked_list_s linked_list; -typedef struct list_item_s list_item; - -typedef struct pending_title_info_s { - FS_MediaType mediaType; - u64 titleId; - u16 version; -} pending_title_info; - -typedef struct populate_pending_titles_data_s { - linked_list* items; - - volatile bool finished; - Result result; - Handle cancelEvent; -} populate_pending_titles_data; - -void task_free_pending_title(list_item* item); -void task_clear_pending_titles(linked_list* items); -Result task_populate_pending_titles(populate_pending_titles_data* data); \ No newline at end of file diff --git a/source/fbi/task/listsystemsavedata.c b/source/fbi/task/listsystemsavedata.c deleted file mode 100644 index d3a9fa7..0000000 --- a/source/fbi/task/listsystemsavedata.c +++ /dev/null @@ -1,120 +0,0 @@ -#include -#include -#include - -#include <3ds.h> - -#include "listsystemsavedata.h" -#include "../resources.h" -#include "../../core/core.h" - -#define MAX_SYSTEM_SAVE_DATA 512 - -static int task_populate_system_save_data_compare_ids(const void* e1, const void* e2) { - u32 id1 = *(u32*) e1; - u32 id2 = *(u32*) e2; - - return id1 > id2 ? 1 : id1 < id2 ? -1 : 0; -} - -static void task_populate_system_save_data_thread(void* arg) { - populate_system_save_data_data* data = (populate_system_save_data_data*) arg; - - Result res = 0; - - u32 systemSaveDataCount = 0; - u32 systemSaveDataIds[MAX_SYSTEM_SAVE_DATA]; - if(R_SUCCEEDED(res = FSUSER_EnumerateSystemSaveData(&systemSaveDataCount, MAX_SYSTEM_SAVE_DATA * sizeof(u32), systemSaveDataIds))) { - qsort(systemSaveDataIds, systemSaveDataCount, sizeof(u32), task_populate_system_save_data_compare_ids); - - for(u32 i = 0; i < systemSaveDataCount && R_SUCCEEDED(res); i++) { - svcWaitSynchronization(task_get_pause_event(), U64_MAX); - if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { - break; - } - - list_item* item = (list_item*) calloc(1, sizeof(list_item)); - if(item != NULL) { - system_save_data_info* systemSaveDataInfo = (system_save_data_info*) calloc(1, sizeof(system_save_data_info)); - if(systemSaveDataInfo != NULL) { - systemSaveDataInfo->systemSaveDataId = systemSaveDataIds[i]; - - snprintf(item->name, LIST_ITEM_NAME_MAX, "%08lX", systemSaveDataIds[i]); - item->color = COLOR_TEXT; - item->data = systemSaveDataInfo; - - linked_list_add(data->items, item); - } else { - free(item); - - res = R_APP_OUT_OF_MEMORY; - } - } else { - res = R_APP_OUT_OF_MEMORY; - } - } - } - - svcCloseHandle(data->cancelEvent); - - data->result = res; - data->finished = true; -} - -void task_free_system_save_data(list_item* item) { - if(item == NULL) { - return; - } - - if(item->data != NULL) { - free(item->data); - } - - free(item); -} - -void task_clear_system_save_data(linked_list* items) { - if(items == NULL) { - return; - } - - linked_list_iter iter; - linked_list_iterate(items, &iter); - - while(linked_list_iter_has_next(&iter)) { - list_item* item = (list_item*) linked_list_iter_next(&iter); - - linked_list_iter_remove(&iter); - task_free_system_save_data(item); - } -} - -Result task_populate_system_save_data(populate_system_save_data_data* data) { - if(data == NULL || data->items == NULL) { - return R_APP_INVALID_ARGUMENT; - } - - task_clear_system_save_data(data->items); - - data->finished = false; - data->result = 0; - data->cancelEvent = 0; - - Result res = 0; - if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, RESET_STICKY))) { - if(threadCreate(task_populate_system_save_data_thread, data, 0x10000, 0x19, 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; - } - } - - return res; -} \ No newline at end of file diff --git a/source/fbi/task/listsystemsavedata.h b/source/fbi/task/listsystemsavedata.h deleted file mode 100644 index 860ff98..0000000 --- a/source/fbi/task/listsystemsavedata.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -typedef struct linked_list_s linked_list; -typedef struct list_item_s list_item; - -typedef struct system_save_data_info_s { - u32 systemSaveDataId; -} system_save_data_info; - -typedef struct populate_system_save_data_data_s { - linked_list* items; - - volatile bool finished; - Result result; - Handle cancelEvent; -} populate_system_save_data_data; - -void task_free_system_save_data(list_item* item); -void task_clear_system_save_data(linked_list* items); -Result task_populate_system_save_data(populate_system_save_data_data* data); \ No newline at end of file diff --git a/source/fbi/task/listtickets.c b/source/fbi/task/listtickets.c deleted file mode 100644 index 852944d..0000000 --- a/source/fbi/task/listtickets.c +++ /dev/null @@ -1,148 +0,0 @@ -#include -#include -#include - -#include <3ds.h> - -#include "listtickets.h" -#include "../resources.h" -#include "../../core/core.h" - -static int task_populate_tickets_compare_ids(const void* e1, const void* e2) { - u64 id1 = *(u64*) e1; - u64 id2 = *(u64*) e2; - - return id1 > id2 ? 1 : id1 < id2 ? -1 : 0; -} - -void task_populate_tickets_update_use(list_item* item) { - if(item == NULL) { - return; - } - - ticket_info* info = (ticket_info*) item->data; - - info->inUse = false; - - AM_TitleEntry entry; - for(FS_MediaType mediaType = MEDIATYPE_NAND; mediaType != MEDIATYPE_GAME_CARD; mediaType++) { - if(R_SUCCEEDED(AM_GetTitleInfo(mediaType, 1, &info->titleId, &entry))) { - info->inUse = true; - break; - } - } - - item->color = info->inUse ? COLOR_TICKET_IN_USE : COLOR_TICKET_NOT_IN_USE; -} - -static void task_populate_tickets_thread(void* arg) { - populate_tickets_data* data = (populate_tickets_data*) arg; - - Result res = 0; - - u32 ticketCount = 0; - if(R_SUCCEEDED(res = AM_GetTicketCount(&ticketCount))) { - u64* ticketIds = (u64*) calloc(ticketCount, sizeof(u64)); - if(ticketIds != NULL) { - if(R_SUCCEEDED(res = AM_GetTicketList(&ticketCount, ticketCount, 0, ticketIds))) { - qsort(ticketIds, ticketCount, sizeof(u64), task_populate_tickets_compare_ids); - - for(u32 i = 0; i < ticketCount && R_SUCCEEDED(res); i++) { - svcWaitSynchronization(task_get_pause_event(), U64_MAX); - if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { - break; - } - - list_item* item = (list_item*) calloc(1, sizeof(list_item)); - if(item != NULL) { - ticket_info* ticketInfo = (ticket_info*) calloc(1, sizeof(ticket_info)); - if(ticketInfo != NULL) { - ticketInfo->titleId = ticketIds[i]; - ticketInfo->loaded = true; - - snprintf(item->name, LIST_ITEM_NAME_MAX, "%016llX", ticketIds[i]); - item->data = ticketInfo; - - task_populate_tickets_update_use(item); - - linked_list_add(data->items, item); - } else { - free(item); - - res = R_APP_OUT_OF_MEMORY; - } - } else { - res = R_APP_OUT_OF_MEMORY; - } - } - } - - free(ticketIds); - } else { - res = R_APP_OUT_OF_MEMORY; - } - } - - svcCloseHandle(data->cancelEvent); - - data->result = res; - data->finished = true; -} - -void task_free_ticket(list_item* item) { - if(item == NULL) { - return; - } - - if(item->data != NULL) { - free(item->data); - } - - free(item); -} - -void task_clear_tickets(linked_list* items) { - if(items == NULL) { - return; - } - - linked_list_iter iter; - linked_list_iterate(items, &iter); - - while(linked_list_iter_has_next(&iter)) { - list_item* item = (list_item*) linked_list_iter_next(&iter); - - linked_list_iter_remove(&iter); - task_free_ticket(item); - } -} - -Result task_populate_tickets(populate_tickets_data* data) { - if(data == NULL || data->items == NULL) { - return R_APP_INVALID_ARGUMENT; - } - - task_clear_tickets(data->items); - - data->finished = false; - data->result = 0; - data->cancelEvent = 0; - - Result res = 0; - if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, RESET_STICKY))) { - if(threadCreate(task_populate_tickets_thread, data, 0x10000, 0x19, 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; - } - } - - return res; -} \ No newline at end of file diff --git a/source/fbi/task/listtickets.h b/source/fbi/task/listtickets.h deleted file mode 100644 index 5462f8b..0000000 --- a/source/fbi/task/listtickets.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -typedef struct linked_list_s linked_list; -typedef struct list_item_s list_item; - -typedef struct ticket_info_s { - bool loaded; - - u64 titleId; - bool inUse; -} ticket_info; - -typedef struct populate_tickets_data_s { - linked_list* items; - - volatile bool finished; - Result result; - Handle cancelEvent; -} populate_tickets_data; - -void task_populate_tickets_update_use(list_item* item); -void task_free_ticket(list_item* item); -void task_clear_tickets(linked_list* items); -Result task_populate_tickets(populate_tickets_data* data); \ No newline at end of file diff --git a/source/fbi/task/listtitles.c b/source/fbi/task/listtitles.c deleted file mode 100644 index 5f37756..0000000 --- a/source/fbi/task/listtitles.c +++ /dev/null @@ -1,359 +0,0 @@ -#include -#include -#include -#include - -#include <3ds.h> - -#include "uitask.h" -#include "listtitles.h" -#include "../resources.h" -#include "../../core/core.h" - -static Result task_populate_titles_add_ctr(populate_titles_data* data, FS_MediaType mediaType, u64 titleId) { - Result res = 0; - - AM_TitleEntry entry; - if(R_SUCCEEDED(res = AM_GetTitleInfo(mediaType, 1, &titleId, &entry))) { - list_item* item = (list_item*) calloc(1, sizeof(list_item)); - if(item != NULL) { - title_info* titleInfo = (title_info*) calloc(1, sizeof(title_info)); - if(titleInfo != NULL) { - titleInfo->mediaType = mediaType; - titleInfo->titleId = titleId; - AM_GetTitleProductCode(mediaType, titleId, titleInfo->productCode); - titleInfo->version = entry.version; - titleInfo->installedSize = entry.size; - titleInfo->twl = false; - titleInfo->hasMeta = false; - - static const u32 filePath[5] = {0x00000000, 0x00000000, 0x00000002, 0x6E6F6369, 0x00000000}; - u32 archivePath[4] = {(u32) (titleId & 0xFFFFFFFF), (u32) ((titleId >> 32) & 0xFFFFFFFF), mediaType, 0x00000000}; - - Handle fileHandle; - if(R_SUCCEEDED(FSUSER_OpenFileDirectly(&fileHandle, ARCHIVE_SAVEDATA_AND_CONTENT, - fs_make_path_binary(archivePath, sizeof(archivePath)), - fs_make_path_binary(filePath, sizeof(filePath)), FS_OPEN_READ, 0))) { - SMDH* smdh = (SMDH*) calloc(1, sizeof(SMDH)); - if(smdh != NULL) { - u32 bytesRead = 0; - if(R_SUCCEEDED(FSFILE_Read(fileHandle, &bytesRead, 0, smdh, sizeof(SMDH))) && bytesRead == sizeof(SMDH)) { - if(smdh->magic[0] == 'S' && smdh->magic[1] == 'M' && smdh->magic[2] == 'D' && smdh->magic[3] == 'H') { - titleInfo->hasMeta = true; - - SMDH_title* smdhTitle = smdh_select_title(smdh); - - utf16_to_utf8((uint8_t*) item->name, smdhTitle->shortDescription, LIST_ITEM_NAME_MAX - 1); - - utf16_to_utf8((uint8_t*) titleInfo->meta.shortDescription, smdhTitle->shortDescription, sizeof(titleInfo->meta.shortDescription) - 1); - utf16_to_utf8((uint8_t*) titleInfo->meta.longDescription, smdhTitle->longDescription, sizeof(titleInfo->meta.longDescription) - 1); - utf16_to_utf8((uint8_t*) titleInfo->meta.publisher, smdhTitle->publisher, sizeof(titleInfo->meta.publisher) - 1); - titleInfo->meta.region = smdh->region; - titleInfo->meta.texture = screen_allocate_free_texture(); - screen_load_texture_tiled(titleInfo->meta.texture, smdh->largeIcon, sizeof(smdh->largeIcon), 48, 48, GPU_RGB565, false); - } - } - - free(smdh); - } - - FSFILE_Close(fileHandle); - } - - if(string_is_empty(item->name)) { - snprintf(item->name, LIST_ITEM_NAME_MAX, "%016llX", titleId); - } - - if(mediaType == MEDIATYPE_NAND) { - item->color = COLOR_NAND; - } else if(mediaType == MEDIATYPE_SD) { - item->color = COLOR_SD; - } else if(mediaType == MEDIATYPE_GAME_CARD) { - item->color = COLOR_GAME_CARD; - } - - item->data = titleInfo; - - linked_list_add_sorted(data->items, item, data->userData, data->compare); - } else { - free(item); - - res = R_APP_OUT_OF_MEMORY; - } - } else { - res = R_APP_OUT_OF_MEMORY; - } - } - - return res; -} - -static Result task_populate_titles_add_twl(populate_titles_data* data, FS_MediaType mediaType, u64 titleId) { - Result res = 0; - - u64 realTitleId = 0; - char productCode[0x10] = {'\0'}; - u16 version = 0; - u64 installedSize = 0; - - u8 header[0x3B4] = {0}; - Result headerRes = FSUSER_GetLegacyRomHeader(mediaType, titleId, header); - - AM_TitleEntry entry; - if(R_SUCCEEDED(res = AM_GetTitleInfo(mediaType, 1, &titleId, &entry))) { - realTitleId = titleId; - AM_GetTitleProductCode(mediaType, titleId, productCode); - version = entry.version; - installedSize = entry.size; - } else if(R_SUCCEEDED(res = headerRes)) { - memcpy(&realTitleId, &header[0x230], sizeof(realTitleId)); - memcpy(productCode, header, 0xC); - version = header[0x01E]; - - u32 size = 0; - if((header[0x012] & 0x2) != 0) { - memcpy(&size, &header[0x210], sizeof(size)); - } else { - memcpy(&size, &header[0x080], sizeof(size)); - } - - installedSize = size; - } - - if(R_SUCCEEDED(res)) { - list_item* item = (list_item*) calloc(1, sizeof(list_item)); - if(item != NULL) { - title_info* titleInfo = (title_info*) calloc(1, sizeof(title_info)); - if(titleInfo != NULL) { - titleInfo->mediaType = mediaType; - titleInfo->titleId = realTitleId; - string_copy(titleInfo->productCode, productCode, sizeof(titleInfo->productCode)); - titleInfo->version = version; - titleInfo->installedSize = installedSize; - titleInfo->twl = true; - titleInfo->hasMeta = false; - - BNR* bnr = (BNR*) calloc(1, sizeof(BNR)); - if(bnr != NULL) { - if(R_SUCCEEDED(FSUSER_GetLegacyBannerData(mediaType, titleId, (u8*) bnr))) { - titleInfo->hasMeta = true; - - char title[0x100] = {'\0'}; - utf16_to_utf8((uint8_t*) title, bnr_select_title(bnr), sizeof(title) - 1); - - if(strchr(title, '\n') == NULL) { - string_copy(item->name, title, sizeof(item->name)); - string_copy(titleInfo->meta.shortDescription, title, sizeof(titleInfo->meta.shortDescription)); - } else { - char* destinations[] = {titleInfo->meta.shortDescription, titleInfo->meta.longDescription, titleInfo->meta.publisher}; - u32 destinationLens[] = {sizeof(titleInfo->meta.shortDescription), sizeof(titleInfo->meta.longDescription), sizeof(titleInfo->meta.publisher)}; - int currDest = 0; - - char* last = title; - char* curr = NULL; - - while(currDest < 3 && (curr = strchr(last, '\n')) != NULL) { - u32 copyLen = curr - last + 1; - if(copyLen > destinationLens[currDest]) { - copyLen = destinationLens[currDest]; - } - - string_copy(destinations[currDest++], last, copyLen); - - last = curr + 1; - *curr = ' '; - } - - string_copy(item->name, title, last - title); - if(currDest < 3) { - string_copy(destinations[currDest], last, destinationLens[currDest]); - } - } - - u8 icon[32 * 32 * 2]; - for(u32 x = 0; x < 32; x++) { - for(u32 y = 0; y < 32; y++) { - u32 srcPos = (((y >> 3) * 4 + (x >> 3)) * 8 + (y & 7)) * 4 + ((x & 7) >> 1); - u32 srcShift = (x & 1) * 4; - u16 srcPx = bnr->mainIconPalette[(bnr->mainIconBitmap[srcPos] >> srcShift) & 0xF]; - - u8 r = (u8) (srcPx & 0x1F); - u8 g = (u8) ((srcPx >> 5) & 0x1F); - u8 b = (u8) ((srcPx >> 10) & 0x1F); - - u16 reversedPx = (u16) ((r << 11) | (g << 6) | (b << 1) | 1); - - u32 dstPos = (y * 32 + x) * 2; - icon[dstPos + 0] = (u8) (reversedPx & 0xFF); - icon[dstPos + 1] = (u8) ((reversedPx >> 8) & 0xFF); - } - } - - if(R_SUCCEEDED(headerRes)) { - memcpy(&titleInfo->meta.region, &header[0x1B0], sizeof(titleInfo->meta.region)); - } else { - titleInfo->meta.region = 0; - } - - titleInfo->meta.texture = screen_allocate_free_texture(); - screen_load_texture_untiled(titleInfo->meta.texture, icon, sizeof(icon), 32, 32, GPU_RGBA5551, false); - } - - free(bnr); - } - - if(string_is_empty(item->name)) { - snprintf(item->name, LIST_ITEM_NAME_MAX, "%016llX", realTitleId); - } - - item->color = COLOR_DS_TITLE; - item->data = titleInfo; - - linked_list_add_sorted(data->items, item, data->userData, data->compare); - } else { - free(item); - - res = R_APP_OUT_OF_MEMORY; - } - } else { - res = R_APP_OUT_OF_MEMORY; - } - } - - return res; -} - -static int task_populate_titles_compare_ids(const void* e1, const void* e2) { - u64 id1 = *(u64*) e1; - u64 id2 = *(u64*) e2; - - return id1 > id2 ? 1 : id1 < id2 ? -1 : 0; -} - -static Result task_populate_titles_from(populate_titles_data* data, FS_MediaType mediaType, bool useDSiWare) { - bool inserted; - FS_CardType type; - if(mediaType == MEDIATYPE_GAME_CARD && (R_FAILED(FSUSER_CardSlotIsInserted(&inserted)) || !inserted || R_FAILED(FSUSER_GetCardType(&type)))) { - return 0; - } - - Result res = 0; - - if(mediaType != MEDIATYPE_GAME_CARD || type == CARD_CTR) { - u32 titleCount = 0; - if(R_SUCCEEDED(res = AM_GetTitleCount(mediaType, &titleCount))) { - u64* titleIds = (u64*) calloc(titleCount, sizeof(u64)); - if(titleIds != NULL) { - if(R_SUCCEEDED(res = AM_GetTitleList(&titleCount, mediaType, titleCount, titleIds))) { - qsort(titleIds, titleCount, sizeof(u64), task_populate_titles_compare_ids); - - for(u32 i = 0; i < titleCount && R_SUCCEEDED(res); i++) { - svcWaitSynchronization(task_get_pause_event(), U64_MAX); - if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { - break; - } - - if(data->filter == NULL || data->filter(data->userData, titleIds[i], mediaType)) { - bool dsiWare = ((titleIds[i] >> 32) & 0x8000) != 0; - if(dsiWare != useDSiWare) { - continue; - } - - res = dsiWare ? task_populate_titles_add_twl(data, mediaType, titleIds[i]) : task_populate_titles_add_ctr(data, mediaType, titleIds[i]); - } - } - } - - free(titleIds); - } else { - res = R_APP_OUT_OF_MEMORY; - } - } - } else { - res = task_populate_titles_add_twl(data, mediaType, 0); - } - - return res; -} - -static void task_populate_titles_thread(void* arg) { - populate_titles_data* data = (populate_titles_data*) arg; - - Result res = 0; - - if(R_SUCCEEDED(res = task_populate_titles_from(data, MEDIATYPE_GAME_CARD, false))) { - if(R_SUCCEEDED(res = task_populate_titles_from(data, MEDIATYPE_SD, false))) { - if(R_SUCCEEDED(res = task_populate_titles_from(data, MEDIATYPE_NAND, false))) { - res = task_populate_titles_from(data, MEDIATYPE_NAND, true); - } - } - } - - svcCloseHandle(data->cancelEvent); - - data->result = res; - data->finished = true; -} - -void task_free_title(list_item* item) { - if(item == NULL) { - return; - } - - if(item->data != NULL) { - title_info* titleInfo = (title_info*) item->data; - if(titleInfo->hasMeta) { - screen_unload_texture(titleInfo->meta.texture); - } - - free(item->data); - } - - free(item); -} - -void task_clear_titles(linked_list* items) { - if(items == NULL) { - return; - } - - linked_list_iter iter; - linked_list_iterate(items, &iter); - - while(linked_list_iter_has_next(&iter)) { - list_item* item = (list_item*) linked_list_iter_next(&iter); - - linked_list_iter_remove(&iter); - task_free_title(item); - } -} - -Result task_populate_titles(populate_titles_data* data) { - if(data == NULL || data->items == NULL) { - return R_APP_INVALID_ARGUMENT; - } - - task_clear_titles(data->items); - - data->finished = false; - data->result = 0; - data->cancelEvent = 0; - - Result res = 0; - if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, RESET_STICKY))) { - if(threadCreate(task_populate_titles_thread, data, 0x10000, 0x19, 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; - } - } - - return res; -} diff --git a/source/fbi/task/listtitles.h b/source/fbi/task/listtitles.h deleted file mode 100644 index 3acd264..0000000 --- a/source/fbi/task/listtitles.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -typedef struct linked_list_s linked_list; -typedef struct list_item_s list_item; - -typedef struct title_info_s { - FS_MediaType mediaType; - u64 titleId; - char productCode[0x10]; - u16 version; - u64 installedSize; - bool twl; - bool hasMeta; - meta_info meta; -} title_info; - -typedef struct populate_titles_data_s { - linked_list* items; - - void* userData; - bool (*filter)(void* data, u64 titleId, FS_MediaType mediaType); - int (*compare)(void* data, const void* p1, const void* p2); - - volatile bool finished; - Result result; - Handle cancelEvent; -} populate_titles_data; - -void task_free_title(list_item* item); -void task_clear_titles(linked_list* items); -Result task_populate_titles(populate_titles_data* data); \ No newline at end of file diff --git a/source/fbi/task/uitask.c b/source/fbi/task/uitask.c deleted file mode 100644 index c999eac..0000000 --- a/source/fbi/task/uitask.c +++ /dev/null @@ -1,265 +0,0 @@ -#include -#include - -#include <3ds.h> - -#include "uitask.h" -#include "../resources.h" -#include "../../core/core.h" - -void task_draw_meta_info(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - meta_info* info = (meta_info*) data; - - u32 metaInfoBoxShadowWidth; - u32 metaInfoBoxShadowHeight; - screen_get_texture_size(&metaInfoBoxShadowWidth, &metaInfoBoxShadowHeight, TEXTURE_META_INFO_BOX_SHADOW); - - float metaInfoBoxShadowX = x1 + (x2 - x1 - metaInfoBoxShadowWidth) / 2; - float metaInfoBoxShadowY = y1 + (y2 - y1) / 4 - metaInfoBoxShadowHeight / 2; - screen_draw_texture(TEXTURE_META_INFO_BOX_SHADOW, metaInfoBoxShadowX, metaInfoBoxShadowY, metaInfoBoxShadowWidth, metaInfoBoxShadowHeight); - - u32 metaInfoBoxWidth; - u32 metaInfoBoxHeight; - screen_get_texture_size(&metaInfoBoxWidth, &metaInfoBoxHeight, TEXTURE_META_INFO_BOX); - - float metaInfoBoxX = x1 + (x2 - x1 - metaInfoBoxWidth) / 2; - float metaInfoBoxY = y1 + (y2 - y1) / 4 - metaInfoBoxHeight / 2; - screen_draw_texture(TEXTURE_META_INFO_BOX, metaInfoBoxX, metaInfoBoxY, metaInfoBoxWidth, metaInfoBoxHeight); - - if(info->texture != 0) { - u32 iconWidth; - u32 iconHeight; - screen_get_texture_size(&iconWidth, &iconHeight, info->texture); - - float iconX = metaInfoBoxX + (64 - iconWidth) / 2; - float iconY = metaInfoBoxY + (metaInfoBoxHeight - iconHeight) / 2; - screen_draw_texture(info->texture, iconX, iconY, iconWidth, iconHeight); - } - - float metaTextX = metaInfoBoxX + 64; - - float shortDescriptionHeight; - screen_get_string_size_wrap(NULL, &shortDescriptionHeight, info->shortDescription, 0.5f, 0.5f, metaInfoBoxX + metaInfoBoxWidth - 8 - metaTextX); - - float longDescriptionHeight; - screen_get_string_size_wrap(NULL, &longDescriptionHeight, info->longDescription, 0.5f, 0.5f, metaInfoBoxX + metaInfoBoxWidth - 8 - metaTextX); - - float publisherHeight; - screen_get_string_size_wrap(NULL, &publisherHeight, info->publisher, 0.5f, 0.5f, metaInfoBoxX + metaInfoBoxWidth - 8 - metaTextX); - - float shortDescriptionY = metaInfoBoxY + (64 - shortDescriptionHeight - 2 - longDescriptionHeight - 2 - publisherHeight) / 2; - screen_draw_string_wrap(info->shortDescription, metaTextX, shortDescriptionY, 0.5f, 0.5f, COLOR_TEXT, false, metaInfoBoxX + metaInfoBoxWidth - 8); - - float longDescriptionY = shortDescriptionY + shortDescriptionHeight + 2; - screen_draw_string_wrap(info->longDescription, metaTextX, longDescriptionY, 0.5f, 0.5f, COLOR_TEXT, false, metaInfoBoxX + metaInfoBoxWidth - 8); - - float publisherY = longDescriptionY + longDescriptionHeight + 2; - screen_draw_string_wrap(info->publisher, metaTextX, publisherY, 0.5f, 0.5f, COLOR_TEXT, false, metaInfoBoxX + metaInfoBoxWidth - 8); -} - -void task_draw_ext_save_data_info(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - ext_save_data_info* info = (ext_save_data_info*) data; - - if(info->hasMeta) { - task_draw_meta_info(view, &info->meta, x1, y1, x2, y2); - } - - char infoText[512]; - - snprintf(infoText, sizeof(infoText), - "追加数据 ID: %016llX\n" - "共享: %s", - info->extSaveDataId, - info->shared ? "是" : "否"); - - float infoWidth; - screen_get_string_size(&infoWidth, NULL, infoText, 0.5f, 0.5f); - - float infoX = x1 + (x2 - x1 - infoWidth) / 2; - float infoY = y1 + (y2 - y1) / 2 - 8; - screen_draw_string(infoText, infoX, infoY, 0.5f, 0.5f, COLOR_TEXT, true); -} - -void task_draw_file_info(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - file_info* info = (file_info*) data; - - char infoText[512]; - size_t infoTextPos = 0; - - if(strlen(info->name) > 48) { - infoTextPos += snprintf(infoText + infoTextPos, sizeof(infoText) - infoTextPos, "名称: %.45s...\n", info->name); - } else { - infoTextPos += snprintf(infoText + infoTextPos, sizeof(infoText) - infoTextPos, "名称: %.48s\n", info->name); - } - - infoTextPos += snprintf(infoText + infoTextPos, sizeof(infoText) - infoTextPos, "属性: "); - - if(info->attributes & (FS_ATTRIBUTE_DIRECTORY | FS_ATTRIBUTE_HIDDEN | FS_ATTRIBUTE_ARCHIVE | FS_ATTRIBUTE_READ_ONLY)) { - bool needsSeparator = false; - - if(info->attributes & FS_ATTRIBUTE_DIRECTORY) { - infoTextPos += snprintf(infoText + infoTextPos, sizeof(infoText) - infoTextPos, "文件夹"); - needsSeparator = true; - } - - if(info->attributes & FS_ATTRIBUTE_HIDDEN) { - if(needsSeparator) { - infoTextPos += snprintf(infoText + infoTextPos, sizeof(infoText) - infoTextPos, ", "); - } - - infoTextPos += snprintf(infoText + infoTextPos, sizeof(infoText) - infoTextPos, "隐藏的项目"); - needsSeparator = true; - } - - if(info->attributes & FS_ATTRIBUTE_ARCHIVE) { - if(needsSeparator) { - infoTextPos += snprintf(infoText + infoTextPos, sizeof(infoText) - infoTextPos, ", "); - } - - infoTextPos += snprintf(infoText + infoTextPos, sizeof(infoText) - infoTextPos, "文件"); - needsSeparator = true; - } - - if(info->attributes & FS_ATTRIBUTE_READ_ONLY) { - if(needsSeparator) { - infoTextPos += snprintf(infoText + infoTextPos, sizeof(infoText) - infoTextPos, ", "); - } - - infoTextPos += snprintf(infoText + infoTextPos, sizeof(infoText) - infoTextPos, "只读"); - needsSeparator = true; - } - } else { - infoTextPos += snprintf(infoText + infoTextPos, sizeof(infoText) - infoTextPos, "无/任意"); - } - - infoTextPos += snprintf(infoText + infoTextPos, sizeof(infoText) - infoTextPos, "\n"); - - if(!(info->attributes & FS_ATTRIBUTE_DIRECTORY)) { - infoTextPos += snprintf(infoText + infoTextPos, sizeof(infoText) - infoTextPos, "大小: %.2f %s", - ui_get_display_size(info->size), ui_get_display_size_units(info->size)); - - infoTextPos += snprintf(infoText + infoTextPos, sizeof(infoText) - infoTextPos, " (%.2f %s)\n", - ui_get_display_size(info->ciaInfo.installedSize), - ui_get_display_size_units(info->ciaInfo.installedSize)); - - if(info->isCia && info->ciaInfo.loaded) { - char regionString[64]; - - if(info->ciaInfo.hasMeta) { - task_draw_meta_info(view, &info->ciaInfo.meta, x1, y1, x2, y2); - - smdh_region_to_string(regionString, info->ciaInfo.meta.region, sizeof(regionString)); - } else { - snprintf(regionString, sizeof(regionString), "未知"); - } - - infoTextPos += snprintf(infoText + infoTextPos, sizeof(infoText) - infoTextPos, - "应用 ID: %016llX\n" - "版本: %hu (%d.%d.%d)\n" - "区域: %s", - //"安装后大小: %.2f %s", - info->ciaInfo.titleId, - info->ciaInfo.version, (info->ciaInfo.version >> 10) & 0x3F, (info->ciaInfo.version >> 4) & 0x3F, info->ciaInfo.version & 0xF, - regionString); - } else if(info->isTicket && info->ticketInfo.loaded) { - infoTextPos += snprintf(infoText + infoTextPos, sizeof(infoText) - infoTextPos, "应用 ID: %016llX", info->ticketInfo.titleId); - } - } - - float infoWidth; - screen_get_string_size(&infoWidth, NULL, infoText, 0.5f, 0.5f); - - float infoX = x1 + (x2 - x1 - infoWidth) / 2; - float infoY = y1 + (y2 - y1) / 2 - 8; - screen_draw_string(infoText, infoX, infoY, 0.5f, 0.5f, COLOR_TEXT, true); -} - -void task_draw_pending_title_info(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - pending_title_info* info = (pending_title_info*) data; - - char infoText[512]; - - snprintf(infoText, sizeof(infoText), - "未完成的应用 ID: %016llX\n" - "位置: %s\n" - "版本: %hu (%d.%d.%d)", - info->titleId, - info->mediaType == MEDIATYPE_NAND ? "NAND" : info->mediaType == MEDIATYPE_SD ? "SD 卡" : "游戏卡带", - info->version, (info->version >> 10) & 0x3F, (info->version >> 4) & 0x3F, info->version & 0xF); - - float infoWidth; - screen_get_string_size(&infoWidth, NULL, infoText, 0.5f, 0.5f); - - float infoX = x1 + (x2 - x1 - infoWidth) / 2; - float infoY = y1 + (y2 - y1) / 2 - 8; - screen_draw_string(infoText, infoX, infoY, 0.5f, 0.5f, COLOR_TEXT, true); -} - -void task_draw_system_save_data_info(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - system_save_data_info* info = (system_save_data_info*) data; - - char infoText[512]; - - snprintf(infoText, sizeof(infoText), "系统数据 ID: %08lX", info->systemSaveDataId); - - float infoWidth; - screen_get_string_size(&infoWidth, NULL, infoText, 0.5f, 0.5f); - - float infoX = x1 + (x2 - x1 - infoWidth) / 2; - float infoY = y1 + (y2 - y1) / 2 - 8; - screen_draw_string(infoText, infoX, infoY, 0.5f, 0.5f, COLOR_TEXT, true); -} - -void task_draw_ticket_info(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - ticket_info* info = (ticket_info*) data; - - if(info->loaded) { - char infoText[512]; - - snprintf(infoText, sizeof(infoText), "应用 ID: %016llX", info->titleId); - - float infoWidth; - screen_get_string_size(&infoWidth, NULL, infoText, 0.5f, 0.5f); - - float infoX = x1 + (x2 - x1 - infoWidth) / 2; - float infoY = y1 + (y2 - y1) / 2 - 8; - screen_draw_string(infoText, infoX, infoY, 0.5f, 0.5f, COLOR_TEXT, true); - } -} - -void task_draw_title_info(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - title_info* info = (title_info*) data; - - char regionString[64]; - - if(info->hasMeta) { - task_draw_meta_info(view, &info->meta, x1, y1, x2, y2); - - smdh_region_to_string(regionString, info->meta.region, sizeof(regionString)); - } else { - snprintf(regionString, sizeof(regionString), "未知"); - } - - char infoText[512]; - - snprintf(infoText, sizeof(infoText), - "应用 ID: %016llX\n" - "位置: %s\n" - "版本: %hu (%d.%d.%d)\n" - "产品代码: %s\n" - "区域: %s\n" - "大小: %.2f %s", - info->titleId, - info->mediaType == MEDIATYPE_NAND ? "NAND" : info->mediaType == MEDIATYPE_SD ? "SD 卡" : "游戏卡带", - info->version, (info->version >> 10) & 0x3F, (info->version >> 4) & 0x3F, info->version & 0xF, - info->productCode, - regionString, - ui_get_display_size(info->installedSize), ui_get_display_size_units(info->installedSize)); - - float infoWidth; - screen_get_string_size(&infoWidth, NULL, infoText, 0.5f, 0.5f); - - float infoX = x1 + (x2 - x1 - infoWidth) / 2; - float infoY = y1 + (y2 - y1) / 2 - 8; - screen_draw_string(infoText, infoX, infoY, 0.5f, 0.5f, COLOR_TEXT, true); -} diff --git a/source/fbi/task/uitask.h b/source/fbi/task/uitask.h deleted file mode 100644 index 2641155..0000000 --- a/source/fbi/task/uitask.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -typedef struct ui_view_s ui_view; - -typedef struct meta_info_s { - char shortDescription[0x100]; - char longDescription[0x200]; - char publisher[0x100]; - u32 region; - u32 texture; -} meta_info; - -void task_draw_meta_info(ui_view* view, void* data, float x1, float y1, float x2, float y2); -void task_draw_ext_save_data_info(ui_view* view, void* data, float x1, float y1, float x2, float y2); -void task_draw_file_info(ui_view* view, void* data, float x1, float y1, float x2, float y2); -void task_draw_pending_title_info(ui_view* view, void* data, float x1, float y1, float x2, float y2); -void task_draw_system_save_data_info(ui_view* view, void* data, float x1, float y1, float x2, float y2); -void task_draw_ticket_info(ui_view* view, void* data, float x1, float y1, float x2, float y2); -void task_draw_title_info(ui_view* view, void* data, float x1, float y1, float x2, float y2); - -#include "listextsavedata.h" -#include "listpendingtitles.h" -#include "listsystemsavedata.h" -#include "listtickets.h" -#include "listtitles.h" -#include "listfiles.h" \ No newline at end of file diff --git a/source/fbi/tickets.c b/source/fbi/tickets.c deleted file mode 100644 index 2ee2d07..0000000 --- a/source/fbi/tickets.c +++ /dev/null @@ -1,141 +0,0 @@ -#include -#include - -#include <3ds.h> - -#include "resources.h" -#include "section.h" -#include "action/action.h" -#include "task/uitask.h" -#include "../core/core.h" - -static list_item delete_ticket = {"删除应用引导表", COLOR_TEXT, action_delete_ticket}; -static list_item delete_unused_tickets = {"删除未使用的应用引导表", COLOR_TEXT, action_delete_tickets_unused}; - -typedef struct { - populate_tickets_data populateData; - - bool populated; -} tickets_data; - -typedef struct { - linked_list* items; - list_item* selected; -} tickets_action_data; - -static void tickets_action_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { - task_draw_ticket_info(view, ((tickets_action_data*) data)->selected->data, x1, y1, x2, y2); -} - -static void tickets_action_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { - tickets_action_data* actionData = (tickets_action_data*) data; - - if(hidKeysDown() & KEY_B) { - ui_pop(); - list_destroy(view); - - free(data); - - return; - } - - if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { - void(*action)(linked_list*, list_item*) = (void(*)(linked_list*, list_item*)) selected->data; - - ui_pop(); - list_destroy(view); - - action(actionData->items, actionData->selected); - - free(data); - - return; - } - - if(linked_list_size(items) == 0) { - linked_list_add(items, &delete_ticket); - linked_list_add(items, &delete_unused_tickets); - } -} - -static void tickets_action_open(linked_list* items, list_item* selected) { - tickets_action_data* data = (tickets_action_data*) calloc(1, sizeof(tickets_action_data)); - if(data == NULL) { - error_display(NULL, NULL, "无法分配应用引导表操作的数据."); - - return; - } - - data->items = items; - data->selected = selected; - - list_display("应用引导表操作", "A: 选择, B: 返回", data, tickets_action_update, tickets_action_draw_top); -} - -static void tickets_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { - if(selected != NULL && selected->data != NULL) { - task_draw_ticket_info(view, selected->data, x1, y1, x2, y2); - } -} - -static void tickets_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { - tickets_data* listData = (tickets_data*) data; - - if(hidKeysDown() & KEY_B) { - if(!listData->populateData.finished) { - svcSignalEvent(listData->populateData.cancelEvent); - while(!listData->populateData.finished) { - svcSleepThread(1000000); - } - } - - ui_pop(); - - task_clear_tickets(items); - list_destroy(view); - - free(listData); - return; - } - - if(!listData->populated || (hidKeysDown() & KEY_X)) { - if(!listData->populateData.finished) { - svcSignalEvent(listData->populateData.cancelEvent); - while(!listData->populateData.finished) { - svcSleepThread(1000000); - } - } - - listData->populateData.items = items; - Result res = task_populate_tickets(&listData->populateData); - if(R_FAILED(res)) { - error_display_res(NULL, NULL, res, "无法启动应用引导表列表填充."); - } - - listData->populated = true; - } - - if(listData->populateData.finished && R_FAILED(listData->populateData.result)) { - error_display_res(NULL, NULL, listData->populateData.result, "无法填充应用引导表列表."); - - listData->populateData.result = 0; - } - - if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { - tickets_action_open(items, selected); - return; - } -} - -void tickets_open() { - tickets_data* data = (tickets_data*) calloc(1, sizeof(tickets_data)); - if(data == NULL) { - error_display(NULL, NULL, "无法分配应用引导表的数据."); - - return; - } - - data->populateData.finished = true; - - list_display("应用引导表", "A: 选择, B: 返回, X: 刷新", data, tickets_update, tickets_draw_top); -} diff --git a/source/fbi/titles.c b/source/fbi/titles.c deleted file mode 100644 index 01c79fd..0000000 --- a/source/fbi/titles.c +++ /dev/null @@ -1,334 +0,0 @@ -#include -#include -#include - -#include <3ds.h> - -#include "resources.h" -#include "section.h" -#include "action/action.h" -#include "task/uitask.h" -#include "../core/core.h" - -static list_item launch_title = {"启动应用", COLOR_TEXT, action_launch_title}; -static list_item delete_title = {"删除应用", COLOR_TEXT, action_delete_title}; -static list_item delete_title_ticket = {"删除应用和应用引导表", COLOR_TEXT, action_delete_title_ticket}; -static list_item extract_smdh = {"提取 SMDH", COLOR_TEXT, action_extract_smdh}; -static list_item import_seed = {"导入种子", COLOR_TEXT, action_import_seed}; -static list_item browse_save_data = {"浏览数据", COLOR_TEXT, action_browse_title_save_data}; -static list_item import_save_data = {"导入数据", COLOR_TEXT, action_import_twl_save}; -static list_item export_save_data = {"导出数据", COLOR_TEXT, action_export_twl_save}; -static list_item erase_save_data = {"清除数据", COLOR_TEXT, action_erase_twl_save}; -static list_item import_secure_value = {"导入安全值", COLOR_TEXT, action_import_secure_value}; -static list_item export_secure_value = {"导出安全值", COLOR_TEXT, action_export_secure_value}; -static list_item delete_secure_value = {"删除安全值", COLOR_TEXT, action_delete_secure_value}; - -typedef struct { - populate_titles_data populateData; - - bool showGameCard; - bool showSD; - bool showNAND; - bool sortById; - bool sortByName; - bool sortBySize; - - bool populated; -} titles_data; - -typedef struct { - linked_list* items; - list_item* selected; -} titles_action_data; - -static void titles_action_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { - task_draw_title_info(view, ((titles_action_data*) data)->selected->data, x1, y1, x2, y2); -} - -static void titles_action_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { - titles_action_data* actionData = (titles_action_data*) data; - - if(hidKeysDown() & KEY_B) { - ui_pop(); - list_destroy(view); - - free(data); - - return; - } - - if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { - void(*action)(linked_list*, list_item*) = (void(*)(linked_list*, list_item*)) selected->data; - - ui_pop(); - list_destroy(view); - - action(actionData->items, actionData->selected); - - free(data); - - return; - } - - if(linked_list_size(items) == 0) { - linked_list_add(items, &launch_title); - - title_info* info = (title_info*) actionData->selected->data; - - if(info->mediaType != MEDIATYPE_GAME_CARD) { - linked_list_add(items, &delete_title); - linked_list_add(items, &delete_title_ticket); - } - - if(!info->twl) { - linked_list_add(items, &extract_smdh); - - if(info->mediaType != MEDIATYPE_GAME_CARD) { - linked_list_add(items, &import_seed); - } - - linked_list_add(items, &browse_save_data); - - if(info->mediaType != MEDIATYPE_GAME_CARD) { - linked_list_add(items, &import_secure_value); - linked_list_add(items, &export_secure_value); - linked_list_add(items, &delete_secure_value); - } - } else if(info->mediaType == MEDIATYPE_GAME_CARD) { - linked_list_add(items, &import_save_data); - linked_list_add(items, &export_save_data); - linked_list_add(items, &erase_save_data); - } - } -} - -static void titles_action_open(linked_list* items, list_item* selected) { - titles_action_data* data = (titles_action_data*) calloc(1, sizeof(titles_action_data)); - if(data == NULL) { - error_display(NULL, NULL, "无法分配应用操作的数据."); - - return; - } - - data->items = items; - data->selected = selected; - - list_display("应用操作", "A: 选择, B: 返回", data, titles_action_update, titles_action_draw_top); -} - -static void titles_options_add_entry(linked_list* items, const char* name, bool* val) { - list_item* item = (list_item*) calloc(1, sizeof(list_item)); - if(item != NULL) { - snprintf(item->name, LIST_ITEM_NAME_MAX, "%s", name); - item->color = *val ? COLOR_ENABLED : COLOR_DISABLED; - item->data = val; - - linked_list_add(items, item); - } -} - -static void titles_options_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { - titles_data* listData = (titles_data*) data; - - if(hidKeysDown() & KEY_B) { - linked_list_iter iter; - linked_list_iterate(items, &iter); - - while(linked_list_iter_has_next(&iter)) { - free(linked_list_iter_next(&iter)); - linked_list_iter_remove(&iter); - } - - ui_pop(); - list_destroy(view); - - return; - } - - if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { - bool* val = (bool*) selected->data; - *val = !(*val); - - if(*val && (val == &listData->sortById || val == &listData->sortByName || val == &listData->sortBySize)) { - if(val == &listData->sortById) { - listData->sortByName = false; - listData->sortBySize = false; - } else if(val == &listData->sortByName) { - listData->sortById = false; - listData->sortBySize = false; - } else if(val == &listData->sortBySize) { - listData->sortById = false; - listData->sortByName = false; - } - - linked_list_iter iter; - linked_list_iterate(items, &iter); - while(linked_list_iter_has_next(&iter)) { - list_item* item = (list_item*) linked_list_iter_next(&iter); - - item->color = *(bool*) item->data ? COLOR_ENABLED : COLOR_DISABLED; - } - } else { - selected->color = *val ? COLOR_ENABLED : COLOR_DISABLED; - } - - listData->populated = false; - } - - if(linked_list_size(items) == 0) { - titles_options_add_entry(items, "显示游戏卡带中的应用", &listData->showGameCard); - titles_options_add_entry(items, "显示 SD 卡中的应用", &listData->showSD); - titles_options_add_entry(items, "显示 NAND 中的应用", &listData->showNAND); - titles_options_add_entry(items, "按 ID 排序", &listData->sortById); - titles_options_add_entry(items, "按名称排序", &listData->sortByName); - titles_options_add_entry(items, "按大小排序", &listData->sortBySize); - } -} - -static void titles_options_open(titles_data* data) { - list_display("选项", "A: 切换, B: 返回", data, titles_options_update, NULL); -} - -static void titles_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { - if(selected != NULL && selected->data != NULL) { - task_draw_title_info(view, selected->data, x1, y1, x2, y2); - } -} - -static void titles_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { - titles_data* listData = (titles_data*) data; - - if(hidKeysDown() & KEY_B) { - if(!listData->populateData.finished) { - svcSignalEvent(listData->populateData.cancelEvent); - while(!listData->populateData.finished) { - svcSleepThread(1000000); - } - } - - ui_pop(); - - task_clear_titles(items); - list_destroy(view); - - free(listData); - return; - } - - if(hidKeysDown() & KEY_SELECT) { - titles_options_open(listData); - return; - } - - if(!listData->populated || (hidKeysDown() & KEY_X)) { - if(!listData->populateData.finished) { - svcSignalEvent(listData->populateData.cancelEvent); - while(!listData->populateData.finished) { - svcSleepThread(1000000); - } - } - - listData->populateData.items = items; - Result res = task_populate_titles(&listData->populateData); - if(R_FAILED(res)) { - error_display_res(NULL, NULL, res, "无法启动应用列表填充."); - } - - listData->populated = true; - } - - if(listData->populateData.finished && R_FAILED(listData->populateData.result)) { - error_display_res(NULL, NULL, listData->populateData.result, "无法填充应用列表."); - - listData->populateData.result = 0; - } - - if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { - titles_action_open(items, selected); - return; - } -} - -static bool titles_filter(void* data, u64 titleId, FS_MediaType mediaType) { - titles_data* listData = (titles_data*) data; - - if(mediaType == MEDIATYPE_GAME_CARD) { - return listData->showGameCard; - } else if(mediaType == MEDIATYPE_SD) { - return listData->showSD; - } else { - return listData->showNAND; - } -} - -static int titles_compare(void* data, const void* p1, const void* p2) { - titles_data* listData = (titles_data*) data; - - list_item* info1 = (list_item*) p1; - list_item* info2 = (list_item*) p2; - - title_info* title1 = (title_info*) info1->data; - title_info* title2 = (title_info*) info2->data; - - - if(title1->mediaType > title2->mediaType) { - return -1; - } else if(title1->mediaType < title2->mediaType) { - return 1; - } else { - if(!title1->twl && title2->twl) { - return -1; - } else if(title1->twl && !title2->twl) { - return 1; - } else { - if(listData->sortById) { - u64 id1 = title1->titleId; - u64 id2 = title2->titleId; - - return id1 > id2 ? 1 : id1 < id2 ? -1 : 0; - } else if(listData->sortByName) { - bool title1HasName = title1->hasMeta && !string_is_empty(title1->meta.shortDescription); - bool title2HasName = title2->hasMeta && !string_is_empty(title2->meta.shortDescription); - - if(title1HasName && !title2HasName) { - return -1; - } else if(!title1HasName && title2HasName) { - return 1; - } else { - return strncasecmp(info1->name, info2->name, sizeof(info1->name)); - } - } else if(listData->sortBySize) { - u64 size1 = title1->installedSize; - u64 size2 = title2->installedSize; - - return size1 > size2 ? -1 : size1 < size2 ? 1 : 0; - } else { - return 0; - } - } - } -} - -void titles_open() { - titles_data* data = (titles_data*) calloc(1, sizeof(titles_data)); - if(data == NULL) { - error_display(NULL, NULL, "无法分配应用的数据."); - - return; - } - - data->populateData.userData = data; - data->populateData.filter = titles_filter; - data->populateData.compare = titles_compare; - - data->populateData.finished = true; - - data->showGameCard = true; - data->showSD = true; - data->showNAND = true; - data->sortById = false; - data->sortByName = true; - data->sortBySize = false; - - list_display("应用", "A: 选择, B: 返回, X: 刷新, SELECT: 选项", data, titles_update, titles_draw_top); -} diff --git a/source/fbi/update.c b/source/fbi/update.c deleted file mode 100644 index 2138e6f..0000000 --- a/source/fbi/update.c +++ /dev/null @@ -1,87 +0,0 @@ -#include -#include -#include - -#include <3ds.h> -#include - -#include "resources.h" -#include "section.h" -#include "action/action.h" -#include "task/uitask.h" -#include "../core/core.h" - -static void update_check_update(ui_view* view, void* data, float* progress, char* text) { - //ui_pop(); - //info_destroy(view); - //prompt_display_notify("检查更新", "测试/滚动版不支持检查更新\n仅预览版及稳定版支持更新至最新版本.", COLOR_TEXT, NULL, NULL, NULL); - //return; - - - bool hasUpdate = false; - char updateURL[DOWNLOAD_URL_MAX]; - - Result res = 0; - - json_t* json = NULL; - if(R_SUCCEEDED(res = http_download_json("https://api.github.com/repos/Theopse/FBI-i18n-zh/releases/latest" /*"https://null"*/, &json, 16 * 1024))) { - if(json_is_object(json)) { - json_t* name = json_object_get(json, "name"); - json_t* assets = json_object_get(json, "assets"); - - if(json_is_string(name) && json_is_array(assets)) { - char versionString[16]; - snprintf(versionString, sizeof(versionString), "%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO); - - if(strncmp(json_string_value(name), versionString, json_string_length(name)) != 0) { - const char* url = NULL; - - for(u32 i = 0; i < json_array_size(assets); i++) { - json_t* val = json_array_get(assets, i); - if(json_is_object(val)) { - json_t* assetName = json_object_get(val, "name"); - json_t* assetUrl = json_object_get(val, "browser_download_url"); - - if(json_is_string(assetName) && json_is_string(assetUrl)) { - if(strncmp(json_string_value(assetName), fs_get_3dsx_path() != NULL ? "FBI.3dsx" : "FBI.cia", json_string_length(assetName)) == 0) { - url = json_string_value(assetUrl); - break; - } - } - } - } - - if(url != NULL) { - string_copy(updateURL, url, DOWNLOAD_URL_MAX); - hasUpdate = true; - } else { - res = R_APP_BAD_DATA; - } - } - } else { - res = R_APP_BAD_DATA; - } - } else { - res = R_APP_BAD_DATA; - } - - json_decref(json); - } - - ui_pop(); - info_destroy(view); - - if(hasUpdate) { - action_install_url("更新 FBI 至最新版本?", updateURL, fs_get_3dsx_path(), NULL, NULL, NULL, NULL); - } else { - if(R_FAILED(res)) { - error_display_res(NULL, NULL, res, "无法检查更新."); - } else { - prompt_display_notify("成功", "无可用的更新.", COLOR_TEXT, NULL, NULL, NULL); - } - } -} - -void update_open() { - info_display("正在检查更新", "", false, NULL, update_check_update, NULL); -} diff --git a/source/libs/quirc/LICENSE b/source/libs/quirc/LICENSE deleted file mode 100644 index d47c026..0000000 --- a/source/libs/quirc/LICENSE +++ /dev/null @@ -1,16 +0,0 @@ -quirc -- QR-code recognition library -Copyright (C) 2010-2012 Daniel Beer - -Permission to use, copy, modify, and/or distribute this software for -any purpose with or without fee is hereby granted, provided that the -above copyright notice and this permission notice appear in all -copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL -WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE -AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL -DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR -PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER -TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. diff --git a/source/libs/quirc/decode.c b/source/libs/quirc/decode.c deleted file mode 100644 index 93520c4..0000000 --- a/source/libs/quirc/decode.c +++ /dev/null @@ -1,918 +0,0 @@ -/* quirc -- QR-code recognition library - * Copyright (C) 2010-2012 Daniel Beer - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "quirc_internal.h" - -#include -#include - -#define MAX_POLY 64 - -/************************************************************************ - * Galois fields - */ - -struct galois_field { - int p; - const uint8_t *log; - const uint8_t *exp; -}; - -static const uint8_t gf16_exp[16] = { - 0x01, 0x02, 0x04, 0x08, 0x03, 0x06, 0x0c, 0x0b, - 0x05, 0x0a, 0x07, 0x0e, 0x0f, 0x0d, 0x09, 0x01 -}; - -static const uint8_t gf16_log[16] = { - 0x00, 0x0f, 0x01, 0x04, 0x02, 0x08, 0x05, 0x0a, - 0x03, 0x0e, 0x09, 0x07, 0x06, 0x0d, 0x0b, 0x0c -}; - -static const struct galois_field gf16 = { - .p = 15, - .log = gf16_log, - .exp = gf16_exp -}; - -static const uint8_t gf256_exp[256] = { - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, - 0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26, - 0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9, - 0x8f, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, - 0x9d, 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35, - 0x6a, 0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23, - 0x46, 0x8c, 0x05, 0x0a, 0x14, 0x28, 0x50, 0xa0, - 0x5d, 0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1, - 0x5f, 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc, - 0x65, 0xca, 0x89, 0x0f, 0x1e, 0x3c, 0x78, 0xf0, - 0xfd, 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f, - 0xfe, 0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2, - 0xd9, 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88, - 0x0d, 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce, - 0x81, 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93, - 0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc, - 0x85, 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9, - 0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54, - 0xa8, 0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa, - 0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73, - 0xe6, 0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e, - 0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff, - 0xe3, 0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4, - 0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41, - 0x82, 0x19, 0x32, 0x64, 0xc8, 0x8d, 0x07, 0x0e, - 0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6, - 0x51, 0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef, - 0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x09, - 0x12, 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5, - 0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0x0b, 0x16, - 0x2c, 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83, - 0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e, 0x01 -}; - -static const uint8_t gf256_log[256] = { - 0x00, 0xff, 0x01, 0x19, 0x02, 0x32, 0x1a, 0xc6, - 0x03, 0xdf, 0x33, 0xee, 0x1b, 0x68, 0xc7, 0x4b, - 0x04, 0x64, 0xe0, 0x0e, 0x34, 0x8d, 0xef, 0x81, - 0x1c, 0xc1, 0x69, 0xf8, 0xc8, 0x08, 0x4c, 0x71, - 0x05, 0x8a, 0x65, 0x2f, 0xe1, 0x24, 0x0f, 0x21, - 0x35, 0x93, 0x8e, 0xda, 0xf0, 0x12, 0x82, 0x45, - 0x1d, 0xb5, 0xc2, 0x7d, 0x6a, 0x27, 0xf9, 0xb9, - 0xc9, 0x9a, 0x09, 0x78, 0x4d, 0xe4, 0x72, 0xa6, - 0x06, 0xbf, 0x8b, 0x62, 0x66, 0xdd, 0x30, 0xfd, - 0xe2, 0x98, 0x25, 0xb3, 0x10, 0x91, 0x22, 0x88, - 0x36, 0xd0, 0x94, 0xce, 0x8f, 0x96, 0xdb, 0xbd, - 0xf1, 0xd2, 0x13, 0x5c, 0x83, 0x38, 0x46, 0x40, - 0x1e, 0x42, 0xb6, 0xa3, 0xc3, 0x48, 0x7e, 0x6e, - 0x6b, 0x3a, 0x28, 0x54, 0xfa, 0x85, 0xba, 0x3d, - 0xca, 0x5e, 0x9b, 0x9f, 0x0a, 0x15, 0x79, 0x2b, - 0x4e, 0xd4, 0xe5, 0xac, 0x73, 0xf3, 0xa7, 0x57, - 0x07, 0x70, 0xc0, 0xf7, 0x8c, 0x80, 0x63, 0x0d, - 0x67, 0x4a, 0xde, 0xed, 0x31, 0xc5, 0xfe, 0x18, - 0xe3, 0xa5, 0x99, 0x77, 0x26, 0xb8, 0xb4, 0x7c, - 0x11, 0x44, 0x92, 0xd9, 0x23, 0x20, 0x89, 0x2e, - 0x37, 0x3f, 0xd1, 0x5b, 0x95, 0xbc, 0xcf, 0xcd, - 0x90, 0x87, 0x97, 0xb2, 0xdc, 0xfc, 0xbe, 0x61, - 0xf2, 0x56, 0xd3, 0xab, 0x14, 0x2a, 0x5d, 0x9e, - 0x84, 0x3c, 0x39, 0x53, 0x47, 0x6d, 0x41, 0xa2, - 0x1f, 0x2d, 0x43, 0xd8, 0xb7, 0x7b, 0xa4, 0x76, - 0xc4, 0x17, 0x49, 0xec, 0x7f, 0x0c, 0x6f, 0xf6, - 0x6c, 0xa1, 0x3b, 0x52, 0x29, 0x9d, 0x55, 0xaa, - 0xfb, 0x60, 0x86, 0xb1, 0xbb, 0xcc, 0x3e, 0x5a, - 0xcb, 0x59, 0x5f, 0xb0, 0x9c, 0xa9, 0xa0, 0x51, - 0x0b, 0xf5, 0x16, 0xeb, 0x7a, 0x75, 0x2c, 0xd7, - 0x4f, 0xae, 0xd5, 0xe9, 0xe6, 0xe7, 0xad, 0xe8, - 0x74, 0xd6, 0xf4, 0xea, 0xa8, 0x50, 0x58, 0xaf -}; - -const static struct galois_field gf256 = { - .p = 255, - .log = gf256_log, - .exp = gf256_exp -}; - -/************************************************************************ - * Polynomial operations - */ - -static void poly_add(uint8_t *dst, const uint8_t *src, uint8_t c, - int shift, const struct galois_field *gf) -{ - int i; - int log_c = gf->log[c]; - - if (!c) - return; - - for (i = 0; i < MAX_POLY; i++) { - int p = i + shift; - uint8_t v = src[i]; - - if (p < 0 || p >= MAX_POLY) - continue; - if (!v) - continue; - - dst[p] ^= gf->exp[(gf->log[v] + log_c) % gf->p]; - } -} - -static uint8_t poly_eval(const uint8_t *s, uint8_t x, - const struct galois_field *gf) -{ - int i; - uint8_t sum = 0; - uint8_t log_x = gf->log[x]; - - if (!x) - return s[0]; - - for (i = 0; i < MAX_POLY; i++) { - uint8_t c = s[i]; - - if (!c) - continue; - - sum ^= gf->exp[(gf->log[c] + log_x * i) % gf->p]; - } - - return sum; -} - -/************************************************************************ - * Berlekamp-Massey algorithm for finding error locator polynomials. - */ - -static void berlekamp_massey(const uint8_t *s, int N, - const struct galois_field *gf, - uint8_t *sigma) -{ - uint8_t C[MAX_POLY]; - uint8_t B[MAX_POLY]; - int L = 0; - int m = 1; - uint8_t b = 1; - int n; - - memset(B, 0, sizeof(B)); - memset(C, 0, sizeof(C)); - B[0] = 1; - C[0] = 1; - - for (n = 0; n < N; n++) { - uint8_t d = s[n]; - uint8_t mult; - int i; - - for (i = 1; i <= L; i++) { - if (!(C[i] && s[n - i])) - continue; - - d ^= gf->exp[(gf->log[C[i]] + - gf->log[s[n - i]]) % - gf->p]; - } - - mult = gf->exp[(gf->p - gf->log[b] + gf->log[d]) % gf->p]; - - if (!d) { - m++; - } else if (L * 2 <= n) { - uint8_t T[MAX_POLY]; - - memcpy(T, C, sizeof(T)); - poly_add(C, B, mult, m, gf); - memcpy(B, T, sizeof(B)); - L = n + 1 - L; - b = d; - m = 1; - } else { - poly_add(C, B, mult, m, gf); - m++; - } - } - - memcpy(sigma, C, MAX_POLY); -} - -/************************************************************************ - * Code stream error correction - * - * Generator polynomial for GF(2^8) is x^8 + x^4 + x^3 + x^2 + 1 - */ - -static int block_syndromes(const uint8_t *data, int bs, int npar, uint8_t *s) -{ - int nonzero = 0; - int i; - - memset(s, 0, MAX_POLY); - - for (i = 0; i < npar; i++) { - int j; - - for (j = 0; j < bs; j++) { - uint8_t c = data[bs - j - 1]; - - if (!c) - continue; - - s[i] ^= gf256_exp[((int)gf256_log[c] + - i * j) % 255]; - } - - if (s[i]) - nonzero = 1; - } - - return nonzero; -} - -static void eloc_poly(uint8_t *omega, - const uint8_t *s, const uint8_t *sigma, - int npar) -{ - int i; - - memset(omega, 0, MAX_POLY); - - for (i = 0; i < npar; i++) { - const uint8_t a = sigma[i]; - const uint8_t log_a = gf256_log[a]; - int j; - - if (!a) - continue; - - for (j = 0; j + 1 < MAX_POLY; j++) { - const uint8_t b = s[j + 1]; - - if (i + j >= npar) - break; - - if (!b) - continue; - - omega[i + j] ^= - gf256_exp[(log_a + gf256_log[b]) % 255]; - } - } -} - -static quirc_decode_error_t correct_block(uint8_t *data, - const struct quirc_rs_params *ecc) -{ - int npar = ecc->bs - ecc->dw; - uint8_t s[MAX_POLY]; - uint8_t sigma[MAX_POLY]; - uint8_t sigma_deriv[MAX_POLY]; - uint8_t omega[MAX_POLY]; - int i; - - /* Compute syndrome vector */ - if (!block_syndromes(data, ecc->bs, npar, s)) - return QUIRC_SUCCESS; - - berlekamp_massey(s, npar, &gf256, sigma); - - /* Compute derivative of sigma */ - memset(sigma_deriv, 0, MAX_POLY); - for (i = 0; i + 1 < MAX_POLY; i += 2) - sigma_deriv[i] = sigma[i + 1]; - - /* Compute error evaluator polynomial */ - eloc_poly(omega, s, sigma, npar - 1); - - /* Find error locations and magnitudes */ - for (i = 0; i < ecc->bs; i++) { - uint8_t xinv = gf256_exp[255 - i]; - - if (!poly_eval(sigma, xinv, &gf256)) { - uint8_t sd_x = poly_eval(sigma_deriv, xinv, &gf256); - uint8_t omega_x = poly_eval(omega, xinv, &gf256); - uint8_t error = gf256_exp[(255 - gf256_log[sd_x] + - gf256_log[omega_x]) % 255]; - - data[ecc->bs - i - 1] ^= error; - } - } - - if (block_syndromes(data, ecc->bs, npar, s)) - return QUIRC_ERROR_DATA_ECC; - - return QUIRC_SUCCESS; -} - -/************************************************************************ - * Format value error correction - * - * Generator polynomial for GF(2^4) is x^4 + x + 1 - */ - -#define FORMAT_MAX_ERROR 3 -#define FORMAT_SYNDROMES (FORMAT_MAX_ERROR * 2) -#define FORMAT_BITS 15 - -static int format_syndromes(uint16_t u, uint8_t *s) -{ - int i; - int nonzero = 0; - - memset(s, 0, MAX_POLY); - - for (i = 0; i < FORMAT_SYNDROMES; i++) { - int j; - - s[i] = 0; - for (j = 0; j < FORMAT_BITS; j++) - if (u & (1 << j)) - s[i] ^= gf16_exp[((i + 1) * j) % 15]; - - if (s[i]) - nonzero = 1; - } - - return nonzero; -} - -static quirc_decode_error_t correct_format(uint16_t *f_ret) -{ - uint16_t u = *f_ret; - int i; - uint8_t s[MAX_POLY]; - uint8_t sigma[MAX_POLY]; - - /* Evaluate U (received codeword) at each of alpha_1 .. alpha_6 - * to get S_1 .. S_6 (but we index them from 0). - */ - if (!format_syndromes(u, s)) - return QUIRC_SUCCESS; - - berlekamp_massey(s, FORMAT_SYNDROMES, &gf16, sigma); - - /* Now, find the roots of the polynomial */ - for (i = 0; i < 15; i++) - if (!poly_eval(sigma, gf16_exp[15 - i], &gf16)) - u ^= (1 << i); - - if (format_syndromes(u, s)) - return QUIRC_ERROR_FORMAT_ECC; - - *f_ret = u; - return QUIRC_SUCCESS; -} - -/************************************************************************ - * Decoder algorithm - */ - -struct datastream { - uint8_t raw[QUIRC_MAX_PAYLOAD]; - int data_bits; - int ptr; - - uint8_t data[QUIRC_MAX_PAYLOAD]; -}; - -static inline int grid_bit(const struct quirc_code *code, int x, int y) -{ - int p = y * code->size + x; - - return (code->cell_bitmap[p >> 3] >> (p & 7)) & 1; -} - -static quirc_decode_error_t read_format(const struct quirc_code *code, - struct quirc_data *data, int which) -{ - int i; - uint16_t format = 0; - uint16_t fdata; - quirc_decode_error_t err; - - if (which) { - for (i = 0; i < 7; i++) - format = (format << 1) | - grid_bit(code, 8, code->size - 1 - i); - for (i = 0; i < 8; i++) - format = (format << 1) | - grid_bit(code, code->size - 8 + i, 8); - } else { - static const int xs[15] = { - 8, 8, 8, 8, 8, 8, 8, 8, 7, 5, 4, 3, 2, 1, 0 - }; - static const int ys[15] = { - 0, 1, 2, 3, 4, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8 - }; - - for (i = 14; i >= 0; i--) - format = (format << 1) | grid_bit(code, xs[i], ys[i]); - } - - format ^= 0x5412; - - err = correct_format(&format); - if (err) - return err; - - fdata = format >> 10; - data->ecc_level = fdata >> 3; - data->mask = fdata & 7; - - return QUIRC_SUCCESS; -} - -static int mask_bit(int mask, int i, int j) -{ - switch (mask) { - case 0: return !((i + j) % 2); - case 1: return !(i % 2); - case 2: return !(j % 3); - case 3: return !((i + j) % 3); - case 4: return !(((i / 2) + (j / 3)) % 2); - case 5: return !((i * j) % 2 + (i * j) % 3); - case 6: return !(((i * j) % 2 + (i * j) % 3) % 2); - case 7: return !(((i * j) % 3 + (i + j) % 2) % 2); - } - - return 0; -} - -static int reserved_cell(int version, int i, int j) -{ - const struct quirc_version_info *ver = &quirc_version_db[version]; - int size = version * 4 + 17; - int ai = -1, aj = -1, a; - - /* Finder + format: top left */ - if (i < 9 && j < 9) - return 1; - - /* Finder + format: bottom left */ - if (i + 8 >= size && j < 9) - return 1; - - /* Finder + format: top right */ - if (i < 9 && j + 8 >= size) - return 1; - - /* Exclude timing patterns */ - if (i == 6 || j == 6) - return 1; - - /* Exclude version info, if it exists. Version info sits adjacent to - * the top-right and bottom-left finders in three rows, bounded by - * the timing pattern. - */ - if (version >= 7) { - if (i < 6 && j + 11 >= size) - return 1; - if (i + 11 >= size && j < 6) - return 1; - } - - /* Exclude alignment patterns */ - for (a = 0; a < QUIRC_MAX_ALIGNMENT && ver->apat[a]; a++) { - int p = ver->apat[a]; - - if (abs(p - i) < 3) - ai = a; - if (abs(p - j) < 3) - aj = a; - } - - if (ai >= 0 && aj >= 0) { - a--; - if (ai > 0 && ai < a) - return 1; - if (aj > 0 && aj < a) - return 1; - if (aj == a && ai == a) - return 1; - } - - return 0; -} - -static void read_bit(const struct quirc_code *code, - struct quirc_data *data, - struct datastream *ds, int i, int j) -{ - int bitpos = ds->data_bits & 7; - int bytepos = ds->data_bits >> 3; - int v = grid_bit(code, j, i); - - if (mask_bit(data->mask, i, j)) - v ^= 1; - - if (v) - ds->raw[bytepos] |= (0x80 >> bitpos); - - ds->data_bits++; -} - -static void read_data(const struct quirc_code *code, - struct quirc_data *data, - struct datastream *ds) -{ - int y = code->size - 1; - int x = code->size - 1; - int dir = -1; - - while (x > 0) { - if (x == 6) - x--; - - if (!reserved_cell(data->version, y, x)) - read_bit(code, data, ds, y, x); - - if (!reserved_cell(data->version, y, x - 1)) - read_bit(code, data, ds, y, x - 1); - - y += dir; - if (y < 0 || y >= code->size) { - dir = -dir; - x -= 2; - y += dir; - } - } -} - -static quirc_decode_error_t codestream_ecc(struct quirc_data *data, - struct datastream *ds) -{ - const struct quirc_version_info *ver = - &quirc_version_db[data->version]; - const struct quirc_rs_params *sb_ecc = &ver->ecc[data->ecc_level]; - struct quirc_rs_params lb_ecc; - int bc = ver->data_bytes / sb_ecc->bs; - int dst_offset = 0; - int lb_count = ver->data_bytes - bc * sb_ecc->bs; - int small_dw_total = bc * sb_ecc->dw; - int i; - - memcpy(&lb_ecc, sb_ecc, sizeof(lb_ecc)); - lb_ecc.dw++; - lb_ecc.bs++; - - for (i = 0; i < bc; i++) { - uint8_t *dst = ds->data + dst_offset; - const struct quirc_rs_params *ecc = sb_ecc; - quirc_decode_error_t err; - int j = 0; - int k; - - for (k = 0; k < sb_ecc->dw; k++) - dst[j++] = ds->raw[k * bc + i]; - - if (i + lb_count >= bc) { - dst[j++] = ds->raw[small_dw_total + i - lb_count]; - ecc = &lb_ecc; - } - - for (k = 0; k < sb_ecc->bs - sb_ecc->dw; k++) - dst[j++] = ds->raw[small_dw_total + lb_count + i + - k * bc]; - - err = correct_block(dst, ecc); - if (err) - return err; - - dst_offset += ecc->dw; - } - - ds->data_bits = dst_offset * 8; - - return QUIRC_SUCCESS; -} - -static inline int bits_remaining(const struct datastream *ds) -{ - return ds->data_bits - ds->ptr; -} - -static int take_bits(struct datastream *ds, int len) -{ - int ret = 0; - - while (len && (ds->ptr < ds->data_bits)) { - uint8_t b = ds->data[ds->ptr >> 3]; - int bitpos = ds->ptr & 7; - - ret <<= 1; - if ((b << bitpos) & 0x80) - ret |= 1; - - ds->ptr++; - len--; - } - - return ret; -} - -static int numeric_tuple(struct quirc_data *data, - struct datastream *ds, - int bits, int digits) -{ - int tuple; - int i; - - if (bits_remaining(ds) < bits) - return -1; - - tuple = take_bits(ds, bits); - - for (i = digits - 1; i >= 0; i--) { - data->payload[data->payload_len + i] = tuple % 10 + '0'; - tuple /= 10; - } - - data->payload_len += digits; - return 0; -} - -static quirc_decode_error_t decode_numeric(struct quirc_data *data, - struct datastream *ds) -{ - int bits = 14; - int count; - - if (data->version < 10) - bits = 10; - else if (data->version < 27) - bits = 12; - - count = take_bits(ds, bits); - if (data->payload_len + count + 1 > QUIRC_MAX_PAYLOAD) - return QUIRC_ERROR_DATA_OVERFLOW; - - while (count >= 3) { - if (numeric_tuple(data, ds, 10, 3) < 0) - return QUIRC_ERROR_DATA_UNDERFLOW; - count -= 3; - } - - if (count >= 2) { - if (numeric_tuple(data, ds, 7, 2) < 0) - return QUIRC_ERROR_DATA_UNDERFLOW; - count -= 2; - } - - if (count) { - if (numeric_tuple(data, ds, 4, 1) < 0) - return QUIRC_ERROR_DATA_UNDERFLOW; - count--; - } - - return QUIRC_SUCCESS; -} - -static int alpha_tuple(struct quirc_data *data, - struct datastream *ds, - int bits, int digits) -{ - int tuple; - int i; - - if (bits_remaining(ds) < bits) - return -1; - - tuple = take_bits(ds, bits); - - for (i = 0; i < digits; i++) { - static const char *alpha_map = - "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; - - data->payload[data->payload_len + digits - i - 1] = - alpha_map[tuple % 45]; - tuple /= 45; - } - - data->payload_len += digits; - return 0; -} - -static quirc_decode_error_t decode_alpha(struct quirc_data *data, - struct datastream *ds) -{ - int bits = 13; - int count; - - if (data->version < 7) - bits = 9; - else if (data->version < 11) - bits = 10; - - count = take_bits(ds, bits); - if (data->payload_len + count + 1 > QUIRC_MAX_PAYLOAD) - return QUIRC_ERROR_DATA_OVERFLOW; - - while (count >= 2) { - if (alpha_tuple(data, ds, 11, 2) < 0) - return QUIRC_ERROR_DATA_UNDERFLOW; - count -= 2; - } - - if (count) { - if (alpha_tuple(data, ds, 6, 1) < 0) - return QUIRC_ERROR_DATA_UNDERFLOW; - count--; - } - - return QUIRC_SUCCESS; -} - -static quirc_decode_error_t decode_byte(struct quirc_data *data, - struct datastream *ds) -{ - int bits = 16; - int count; - int i; - - if (data->version < 10) - bits = 8; - - count = take_bits(ds, bits); - if (data->payload_len + count + 1 > QUIRC_MAX_PAYLOAD) - return QUIRC_ERROR_DATA_OVERFLOW; - if (bits_remaining(ds) < count * 8) - return QUIRC_ERROR_DATA_UNDERFLOW; - - for (i = 0; i < count; i++) - data->payload[data->payload_len++] = take_bits(ds, 8); - - return QUIRC_SUCCESS; -} - -static quirc_decode_error_t decode_kanji(struct quirc_data *data, - struct datastream *ds) -{ - int bits = 12; - int count; - int i; - - if (data->version < 10) - bits = 8; - else if (data->version < 27) - bits = 10; - - count = take_bits(ds, bits); - if (data->payload_len + count * 2 + 1 > QUIRC_MAX_PAYLOAD) - return QUIRC_ERROR_DATA_OVERFLOW; - if (bits_remaining(ds) < count * 13) - return QUIRC_ERROR_DATA_UNDERFLOW; - - for (i = 0; i < count; i++) { - int d = take_bits(ds, 13); - uint16_t sjw; - - if (d + 0x8140 >= 0x9ffc) - sjw = d + 0x8140; - else - sjw = d + 0xc140; - - data->payload[data->payload_len++] = sjw >> 8; - data->payload[data->payload_len++] = sjw & 0xff; - } - - return QUIRC_SUCCESS; -} - -static quirc_decode_error_t decode_eci(struct quirc_data *data, - struct datastream *ds) -{ - if (bits_remaining(ds) < 8) - return QUIRC_ERROR_DATA_UNDERFLOW; - - data->eci = take_bits(ds, 8); - - if ((data->eci & 0xc0) == 0x80) { - if (bits_remaining(ds) < 8) - return QUIRC_ERROR_DATA_UNDERFLOW; - - data->eci = (data->eci << 8) | take_bits(ds, 8); - } else if ((data->eci & 0xe0) == 0xc0) { - if (bits_remaining(ds) < 16) - return QUIRC_ERROR_DATA_UNDERFLOW; - - data->eci = (data->eci << 16) | take_bits(ds, 16); - } - - return QUIRC_SUCCESS; -} - -static quirc_decode_error_t decode_payload(struct quirc_data *data, - struct datastream *ds) -{ - while (bits_remaining(ds) >= 4) { - quirc_decode_error_t err = QUIRC_SUCCESS; - int type = take_bits(ds, 4); - - switch (type) { - case QUIRC_DATA_TYPE_NUMERIC: - err = decode_numeric(data, ds); - break; - - case QUIRC_DATA_TYPE_ALPHA: - err = decode_alpha(data, ds); - break; - - case QUIRC_DATA_TYPE_BYTE: - err = decode_byte(data, ds); - break; - - case QUIRC_DATA_TYPE_KANJI: - err = decode_kanji(data, ds); - break; - - case 7: - err = decode_eci(data, ds); - break; - - default: - goto done; - } - - if (err) - return err; - - if (!(type & (type - 1)) && (type > data->data_type)) - data->data_type = type; - } -done: - - /* Add nul terminator to all payloads */ - if (data->payload_len >= sizeof(data->payload)) - data->payload_len--; - data->payload[data->payload_len] = 0; - - return QUIRC_SUCCESS; -} - -quirc_decode_error_t quirc_decode(const struct quirc_code *code, - struct quirc_data *data) -{ - quirc_decode_error_t err; - struct datastream ds; - - if ((code->size - 17) % 4) - return QUIRC_ERROR_INVALID_GRID_SIZE; - - memset(data, 0, sizeof(*data)); - memset(&ds, 0, sizeof(ds)); - - data->version = (code->size - 17) / 4; - - if (data->version < 1 || - data->version > QUIRC_MAX_VERSION) - return QUIRC_ERROR_INVALID_VERSION; - - /* Read format information -- try both locations */ - err = read_format(code, data, 0); - if (err) - err = read_format(code, data, 1); - if (err) - return err; - - read_data(code, data, &ds); - err = codestream_ecc(data, &ds); - if (err) - return err; - - err = decode_payload(data, &ds); - if (err) - return err; - - return QUIRC_SUCCESS; -} diff --git a/source/libs/quirc/identify.c b/source/libs/quirc/identify.c deleted file mode 100644 index 8646989..0000000 --- a/source/libs/quirc/identify.c +++ /dev/null @@ -1,1265 +0,0 @@ -/* quirc - QR-code recognition library - * Copyright (C) 2010-2012 Daniel Beer - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include -#include -#include "quirc_internal.h" - -/************************************************************************ - * Linear algebra routines - */ - -static int line_intersect(const struct quirc_point *p0, - const struct quirc_point *p1, - const struct quirc_point *q0, - const struct quirc_point *q1, - struct quirc_point *r) -{ - /* (a, b) is perpendicular to line p */ - int a = -(p1->y - p0->y); - int b = p1->x - p0->x; - - /* (c, d) is perpendicular to line q */ - int c = -(q1->y - q0->y); - int d = q1->x - q0->x; - - /* e and f are dot products of the respective vectors with p and q */ - int e = a * p1->x + b * p1->y; - int f = c * q1->x + d * q1->y; - - /* Now we need to solve: - * [a b] [rx] [e] - * [c d] [ry] = [f] - * - * We do this by inverting the matrix and applying it to (e, f): - * [ d -b] [e] [rx] - * 1/det [-c a] [f] = [ry] - */ - int det = (a * d) - (b * c); - - if (!det) - return 0; - - r->x = (d * e - b * f) / det; - r->y = (-c * e + a * f) / det; - - return 1; -} - -static void perspective_setup(double *c, - const struct quirc_point *rect, - double w, double h) -{ - double x0 = rect[0].x; - double y0 = rect[0].y; - double x1 = rect[1].x; - double y1 = rect[1].y; - double x2 = rect[2].x; - double y2 = rect[2].y; - double x3 = rect[3].x; - double y3 = rect[3].y; - - double wden = w * (x2*y3 - x3*y2 + (x3-x2)*y1 + x1*(y2-y3)); - double hden = h * (x2*y3 + x1*(y2-y3) - x3*y2 + (x3-x2)*y1); - - c[0] = (x1*(x2*y3-x3*y2) + x0*(-x2*y3+x3*y2+(x2-x3)*y1) + - x1*(x3-x2)*y0) / wden; - c[1] = -(x0*(x2*y3+x1*(y2-y3)-x2*y1) - x1*x3*y2 + x2*x3*y1 - + (x1*x3-x2*x3)*y0) / hden; - c[2] = x0; - c[3] = (y0*(x1*(y3-y2)-x2*y3+x3*y2) + y1*(x2*y3-x3*y2) + - x0*y1*(y2-y3)) / wden; - c[4] = (x0*(y1*y3-y2*y3) + x1*y2*y3 - x2*y1*y3 + - y0*(x3*y2-x1*y2+(x2-x3)*y1)) / hden; - c[5] = y0; - c[6] = (x1*(y3-y2) + x0*(y2-y3) + (x2-x3)*y1 + (x3-x2)*y0) / wden; - c[7] = (-x2*y3 + x1*y3 + x3*y2 + x0*(y1-y2) - x3*y1 + (x2-x1)*y0) / - hden; -} - -static void perspective_map(const double *c, - double u, double v, struct quirc_point *ret) -{ - double den = c[6]*u + c[7]*v + 1.0; - double x = (c[0]*u + c[1]*v + c[2]) / den; - double y = (c[3]*u + c[4]*v + c[5]) / den; - - ret->x = rint(x); - ret->y = rint(y); -} - -static void perspective_unmap(const double *c, - const struct quirc_point *in, - double *u, double *v) -{ - double x = in->x; - double y = in->y; - double den = -c[0]*c[7]*y + c[1]*c[6]*y + (c[3]*c[7]-c[4]*c[6])*x + - c[0]*c[4] - c[1]*c[3]; - - *u = -(c[1]*(y-c[5]) - c[2]*c[7]*y + (c[5]*c[7]-c[4])*x + c[2]*c[4]) / - den; - *v = (c[0]*(y-c[5]) - c[2]*c[6]*y + (c[5]*c[6]-c[3])*x + c[2]*c[3]) / - den; -} - -/************************************************************************ - * Span-based floodfill routine - */ - -typedef void (*span_func_t)(void *user_data, int y, int left, int right); - -#if 0 // recursive flood fill - -#define FLOOD_FILL_MAX_DEPTH 4096 - -struct flood_fill_params{ - struct quirc *q; - int from; - int to; - span_func_t func; - void *user_data; -}; - -static struct flood_fill_params ffp; - -static void flood_fill_rec(int x, int y, int depth) -{ - int left = x; - int right = x; - int i; - quirc_pixel_t *row = ffp.q->pixels + y * ffp.q->w; - - if (!depth) - return; - - while (left > 0 && row[left - 1] == ffp.from) - left--; - - while (right < ffp.q->w - 1 && row[right + 1] == ffp.from) - right++; - - /* Fill the extent */ - for (i = left; i <= right; i++) - row[i] = ffp.to; - - if (ffp.func) - ffp.func(ffp.user_data, y, left, right); - - /* Seed new flood-fills */ - if (y > 0) { - row = ffp.q->pixels + (y - 1) * ffp.q->w; - - for (i = left; i <= right; i++) - if (row[i] == ffp.from) - flood_fill_rec(i, y - 1, depth - 1); - } - - if (y < ffp.q->h - 1) { - row = ffp.q->pixels + (y + 1) * ffp.q->w; - - for (i = left; i <= right; i++) - if (row[i] == ffp.from) - flood_fill_rec(i, y + 1, depth - 1); - } -} - -static void flood_fill_seed(struct quirc *q, int x, int y, int from, int to, - span_func_t func, void *user_data) -{ - ffp.q = q; - ffp.from = from; - ffp.to = to; - ffp.func = func; - ffp.user_data = user_data; - - flood_fill_rec(x, y, FLOOD_FILL_MAX_DEPTH); -} - -#else // stacked flood fill - -#define FILL_STACK_CHUNK_SIZE 0x400 - -struct fill_stack_chunk{ - int x[FILL_STACK_CHUNK_SIZE]; - int y[FILL_STACK_CHUNK_SIZE]; - struct fill_stack_chunk *prev; -}; - -struct fill_stack{ - struct fill_stack_chunk *last_chunk; - int index; -}; - -static void fill_stack_init(struct fill_stack *s){ - s->last_chunk = NULL; -} - -static int fill_stack_is_empty(struct fill_stack *s){ - return s->last_chunk == NULL; -} - -static void fill_stack_push(struct fill_stack *s, int x, int y){ - struct fill_stack_chunk *c; - if(s->last_chunk != NULL && s->index < FILL_STACK_CHUNK_SIZE - 1){ - c = s->last_chunk; - s->index++; - }else{ - c = (struct fill_stack_chunk*)malloc(sizeof(struct fill_stack_chunk)); - if(c == NULL){ - return; - } - c->prev = s->last_chunk; - s->last_chunk = c; - s->index = 0; - } - c->x[s->index] = x; - c->y[s->index] = y; -} - -static void fill_stack_pop(struct fill_stack *s, int *px, int *py){ - struct fill_stack_chunk *c = s->last_chunk; - if(c == NULL){ - return; - } - *px = c->x[s->index]; - *py = c->y[s->index]; - if(s->index > 0){ - s->index--; - }else{ - s->last_chunk = c->prev; - s->index = FILL_STACK_CHUNK_SIZE - 1; - free(c); - } -} - -static void flood_fill_seed(struct quirc *q, int start_x, int start_y, int from, int to, - span_func_t func, void *user_data) -{ - struct fill_stack s; - fill_stack_init(&s); - fill_stack_push(&s, start_x, start_y); - - do{ - int x = 0, y = 0; - fill_stack_pop(&s, &x, &y); - - int left = x, right = x, i; - quirc_pixel_t *row = q->pixels + y * q->w; - - while (left > 0 && row[left - 1] == from) - left--; - - while (right < q->w - 1 && row[right + 1] == from) - right++; - - /* Fill the extent */ - for (i = left; i <= right; i++) - row[i] = to; - - if (func) - func(user_data, y, left, right); - - /* Seed new flood-fills */ - if (y > 0) { - row = q->pixels + (y - 1) * q->w; - - for (i = left; i <= right; i++) - if (row[i] == from) - fill_stack_push(&s, i, y - 1); - } - - if (y < q->h - 1) { - row = q->pixels + (y + 1) * q->w; - - for (i = left; i <= right; i++) - if (row[i] == from) - fill_stack_push(&s, i, y + 1); - } - }while(!fill_stack_is_empty(&s)); -} -#endif - -/************************************************************************ - * Adaptive thresholding - */ - -#define THRESHOLD_S_DEN 8 -#define THRESHOLD_T 5 - -static void threshold(struct quirc *q) -{ - int x, y; - int avg_w = 0; - int avg_u = 0; - int threshold_s = q->w / THRESHOLD_S_DEN; - quirc_pixel_t *row = q->pixels; - - for (y = 0; y < q->h; y++) { - int row_average[q->w]; - - memset(row_average, 0, sizeof(row_average)); - - for (x = 0; x < q->w; x++) { - int w, u; - - if (y & 1) { - w = x; - u = q->w - 1 - x; - } else { - w = q->w - 1 - x; - u = x; - } - - avg_w = (avg_w * (threshold_s - 1)) / - threshold_s + row[w]; - avg_u = (avg_u * (threshold_s - 1)) / - threshold_s + row[u]; - - row_average[w] += avg_w; - row_average[u] += avg_u; - } - - for (x = 0; x < q->w; x++) { - if (row[x] < row_average[x] * - (100 - THRESHOLD_T) / (200 * threshold_s)) - row[x] = QUIRC_PIXEL_BLACK; - else - row[x] = QUIRC_PIXEL_WHITE; - } - - row += q->w; - } -} - -static void area_count(void *user_data, int y, int left, int right) -{ - ((struct quirc_region *)user_data)->count += right - left + 1; -} - -static int region_code(struct quirc *q, int x, int y) -{ - int pixel; - struct quirc_region *box; - int region; - - if (x < 0 || y < 0 || x >= q->w || y >= q->h) - return -1; - - pixel = q->pixels[y * q->w + x]; - - if (pixel >= QUIRC_PIXEL_REGION) - return pixel; - - if (pixel == QUIRC_PIXEL_WHITE) - return -1; - - if (q->num_regions >= QUIRC_MAX_REGIONS) - return -1; - - region = q->num_regions; - box = &q->regions[q->num_regions++]; - - memset(box, 0, sizeof(*box)); - - box->seed.x = x; - box->seed.y = y; - box->capstone = -1; - - flood_fill_seed(q, x, y, pixel, region, area_count, box); - - return region; -} - -struct polygon_score_data { - struct quirc_point ref; - - int scores[4]; - struct quirc_point *corners; -}; - -static void find_one_corner(void *user_data, int y, int left, int right) -{ - struct polygon_score_data *psd = - (struct polygon_score_data *)user_data; - int xs[2] = {left, right}; - int dy = y - psd->ref.y; - int i; - - for (i = 0; i < 2; i++) { - int dx = xs[i] - psd->ref.x; - int d = dx * dx + dy * dy; - - if (d > psd->scores[0]) { - psd->scores[0] = d; - psd->corners[0].x = xs[i]; - psd->corners[0].y = y; - } - } -} - -static void find_other_corners(void *user_data, int y, int left, int right) -{ - struct polygon_score_data *psd = - (struct polygon_score_data *)user_data; - int xs[2] = {left, right}; - int i; - - for (i = 0; i < 2; i++) { - int up = xs[i] * psd->ref.x + y * psd->ref.y; - int right = xs[i] * -psd->ref.y + y * psd->ref.x; - int scores[4] = {up, right, -up, -right}; - int j; - - for (j = 0; j < 4; j++) { - if (scores[j] > psd->scores[j]) { - psd->scores[j] = scores[j]; - psd->corners[j].x = xs[i]; - psd->corners[j].y = y; - } - } - } -} - -static void find_region_corners(struct quirc *q, - int rcode, const struct quirc_point *ref, - struct quirc_point *corners) -{ - struct quirc_region *region = &q->regions[rcode]; - struct polygon_score_data psd; - int i; - - memset(&psd, 0, sizeof(psd)); - psd.corners = corners; - - memcpy(&psd.ref, ref, sizeof(psd.ref)); - psd.scores[0] = -1; - flood_fill_seed(q, region->seed.x, region->seed.y, - rcode, QUIRC_PIXEL_BLACK, - find_one_corner, &psd); - - psd.ref.x = psd.corners[0].x - psd.ref.x; - psd.ref.y = psd.corners[0].y - psd.ref.y; - - for (i = 0; i < 4; i++) - memcpy(&psd.corners[i], ®ion->seed, - sizeof(psd.corners[i])); - - i = region->seed.x * psd.ref.x + region->seed.y * psd.ref.y; - psd.scores[0] = i; - psd.scores[2] = -i; - i = region->seed.x * -psd.ref.y + region->seed.y * psd.ref.x; - psd.scores[1] = i; - psd.scores[3] = -i; - - flood_fill_seed(q, region->seed.x, region->seed.y, - QUIRC_PIXEL_BLACK, rcode, - find_other_corners, &psd); -} - -static void record_capstone(struct quirc *q, int ring, int stone) -{ - struct quirc_region *stone_reg = &q->regions[stone]; - struct quirc_region *ring_reg = &q->regions[ring]; - struct quirc_capstone *capstone; - int cs_index; - - if (q->num_capstones >= QUIRC_MAX_CAPSTONES) - return; - - cs_index = q->num_capstones; - capstone = &q->capstones[q->num_capstones++]; - - memset(capstone, 0, sizeof(*capstone)); - - capstone->qr_grid = -1; - capstone->ring = ring; - capstone->stone = stone; - stone_reg->capstone = cs_index; - ring_reg->capstone = cs_index; - - /* Find the corners of the ring */ - find_region_corners(q, ring, &stone_reg->seed, capstone->corners); - - /* Set up the perspective transform and find the center */ - perspective_setup(capstone->c, capstone->corners, 7.0, 7.0); - perspective_map(capstone->c, 3.5, 3.5, &capstone->center); -} - -static void test_capstone(struct quirc *q, int x, int y, int *pb) -{ - int ring_right = region_code(q, x - pb[4], y); - int stone = region_code(q, x - pb[4] - pb[3] - pb[2], y); - int ring_left = region_code(q, x - pb[4] - pb[3] - - pb[2] - pb[1] - pb[0], - y); - struct quirc_region *stone_reg; - struct quirc_region *ring_reg; - int ratio; - - if (ring_left < 0 || ring_right < 0 || stone < 0) - return; - - /* Left and ring of ring should be connected */ - if (ring_left != ring_right) - return; - - /* Ring should be disconnected from stone */ - if (ring_left == stone) - return; - - stone_reg = &q->regions[stone]; - ring_reg = &q->regions[ring_left]; - - /* Already detected */ - if (stone_reg->capstone >= 0 || ring_reg->capstone >= 0) - return; - - /* Ratio should ideally be 37.5 */ - ratio = stone_reg->count * 100 / ring_reg->count; - if (ratio < 10 || ratio > 70) - return; - - record_capstone(q, ring_left, stone); -} - -static void finder_scan(struct quirc *q, int y) -{ - quirc_pixel_t *row = q->pixels + y * q->w; - int x; - int last_color; - int run_length = 0; - int run_count = 0; - int pb[5]; - - memset(pb, 0, sizeof(pb)); - for (x = 0; x < q->w; x++) { - int color = row[x] ? 1 : 0; - - if (x && color != last_color) { - memmove(pb, pb + 1, sizeof(pb[0]) * 4); - pb[4] = run_length; - run_length = 0; - run_count++; - - if (!color && run_count >= 5) { - static int check[5] = {1, 1, 3, 1, 1}; - int avg, err; - int i; - int ok = 1; - - avg = (pb[0] + pb[1] + pb[3] + pb[4]) / 4; - err = avg * 3 / 4; - - for (i = 0; i < 5; i++) - if (pb[i] < check[i] * avg - err || - pb[i] > check[i] * avg + err) - ok = 0; - - if (ok) - test_capstone(q, x, y, pb); - } - } - - run_length++; - last_color = color; - } -} - -static void find_alignment_pattern(struct quirc *q, int index) -{ - struct quirc_grid *qr = &q->grids[index]; - struct quirc_capstone *c0 = &q->capstones[qr->caps[0]]; - struct quirc_capstone *c2 = &q->capstones[qr->caps[2]]; - struct quirc_point a; - struct quirc_point b; - struct quirc_point c; - int size_estimate; - int step_size = 1; - int dir = 0; - double u, v; - - /* Grab our previous estimate of the alignment pattern corner */ - memcpy(&b, &qr->align, sizeof(b)); - - /* Guess another two corners of the alignment pattern so that we - * can estimate its size. - */ - perspective_unmap(c0->c, &b, &u, &v); - perspective_map(c0->c, u, v + 1.0, &a); - perspective_unmap(c2->c, &b, &u, &v); - perspective_map(c2->c, u + 1.0, v, &c); - - size_estimate = abs((a.x - b.x) * -(c.y - b.y) + - (a.y - b.y) * (c.x - b.x)); - - /* Spiral outwards from the estimate point until we find something - * roughly the right size. Don't look too far from the estimate - * point. - */ - while (step_size * step_size < size_estimate * 100) { - static const int dx_map[] = {1, 0, -1, 0}; - static const int dy_map[] = {0, -1, 0, 1}; - int i; - - for (i = 0; i < step_size; i++) { - int code = region_code(q, b.x, b.y); - - if (code >= 0) { - struct quirc_region *reg = &q->regions[code]; - - if (reg->count >= size_estimate / 2 && - reg->count <= size_estimate * 2) { - qr->align_region = code; - return; - } - } - - b.x += dx_map[dir]; - b.y += dy_map[dir]; - } - - dir = (dir + 1) % 4; - if (!(dir & 1)) - step_size++; - } -} - -static void find_leftmost_to_line(void *user_data, int y, int left, int right) -{ - struct polygon_score_data *psd = - (struct polygon_score_data *)user_data; - int xs[2] = {left, right}; - int i; - - for (i = 0; i < 2; i++) { - int d = -psd->ref.y * xs[i] + psd->ref.x * y; - - if (d < psd->scores[0]) { - psd->scores[0] = d; - psd->corners[0].x = xs[i]; - psd->corners[0].y = y; - } - } -} - -/* Do a Bresenham scan from one point to another and count the number - * of black/white transitions. - */ -static int timing_scan(const struct quirc *q, - const struct quirc_point *p0, - const struct quirc_point *p1) -{ - int n = p1->x - p0->x; - int d = p1->y - p0->y; - int x = p0->x; - int y = p0->y; - int *dom, *nondom; - int dom_step; - int nondom_step; - int a = 0; - int i; - int run_length = 0; - int count = 0; - - if (p0->x < 0 || p0->y < 0 || p0->x >= q->w || p0->y >= q->h) - return -1; - if (p1->x < 0 || p1->y < 0 || p1->x >= q->w || p1->y >= q->h) - return -1; - - if (abs(n) > abs(d)) { - int swap = n; - - n = d; - d = swap; - - dom = &x; - nondom = &y; - } else { - dom = &y; - nondom = &x; - } - - if (n < 0) { - n = -n; - nondom_step = -1; - } else { - nondom_step = 1; - } - - if (d < 0) { - d = -d; - dom_step = -1; - } else { - dom_step = 1; - } - - x = p0->x; - y = p0->y; - for (i = 0; i <= d; i++) { - int pixel; - - if (y < 0 || y >= q->h || x < 0 || x >= q->w) - break; - - pixel = q->pixels[y * q->w + x]; - - if (pixel) { - if (run_length >= 2) - count++; - run_length = 0; - } else { - run_length++; - } - - a += n; - *dom += dom_step; - if (a >= d) { - *nondom += nondom_step; - a -= d; - } - } - - return count; -} - -/* Try the measure the timing pattern for a given QR code. This does - * not require the global perspective to have been set up, but it - * does require that the capstone corners have been set to their - * canonical rotation. - * - * For each capstone, we find a point in the middle of the ring band - * which is nearest the centre of the code. Using these points, we do - * a horizontal and a vertical timing scan. - */ -static int measure_timing_pattern(struct quirc *q, int index) -{ - struct quirc_grid *qr = &q->grids[index]; - int i; - int scan; - int ver; - int size; - - for (i = 0; i < 3; i++) { - static const double us[] = {6.5, 6.5, 0.5}; - static const double vs[] = {0.5, 6.5, 6.5}; - struct quirc_capstone *cap = &q->capstones[qr->caps[i]]; - - perspective_map(cap->c, us[i], vs[i], &qr->tpep[i]); - } - - qr->hscan = timing_scan(q, &qr->tpep[1], &qr->tpep[2]); - qr->vscan = timing_scan(q, &qr->tpep[1], &qr->tpep[0]); - - scan = qr->hscan; - if (qr->vscan > scan) - scan = qr->vscan; - - /* If neither scan worked, we can't go any further. */ - if (scan < 0) - return -1; - - /* Choose the nearest allowable grid size */ - size = scan * 2 + 13; - ver = (size - 15) / 4; - qr->grid_size = ver * 4 + 17; - - return 0; -} - -/* Read a cell from a grid using the currently set perspective - * transform. Returns +/- 1 for black/white, 0 for cells which are - * out of image bounds. - */ -static int read_cell(const struct quirc *q, int index, int x, int y) -{ - const struct quirc_grid *qr = &q->grids[index]; - struct quirc_point p; - - perspective_map(qr->c, x + 0.5, y + 0.5, &p); - if (p.y < 0 || p.y >= q->h || p.x < 0 || p.x >= q->w) - return 0; - - return q->pixels[p.y * q->w + p.x] ? 1 : -1; -} - -static int fitness_cell(const struct quirc *q, int index, int x, int y) -{ - const struct quirc_grid *qr = &q->grids[index]; - int score = 0; - int u, v; - - for (v = 0; v < 3; v++) - for (u = 0; u < 3; u++) { - static const double offsets[] = {0.3, 0.5, 0.7}; - struct quirc_point p; - - perspective_map(qr->c, x + offsets[u], - y + offsets[v], &p); - if (p.y < 0 || p.y >= q->h || p.x < 0 || p.x >= q->w) - continue; - - if (q->pixels[p.y * q->w + p.x]) - score++; - else - score--; - } - - return score; -} - -static int fitness_ring(const struct quirc *q, int index, int cx, int cy, - int radius) -{ - int i; - int score = 0; - - for (i = 0; i < radius * 2; i++) { - score += fitness_cell(q, index, cx - radius + i, cy - radius); - score += fitness_cell(q, index, cx - radius, cy + radius - i); - score += fitness_cell(q, index, cx + radius, cy - radius + i); - score += fitness_cell(q, index, cx + radius - i, cy + radius); - } - - return score; -} - -static int fitness_apat(const struct quirc *q, int index, int cx, int cy) -{ - return fitness_cell(q, index, cx, cy) - - fitness_ring(q, index, cx, cy, 1) + - fitness_ring(q, index, cx, cy, 2); -} - -static int fitness_capstone(const struct quirc *q, int index, int x, int y) -{ - x += 3; - y += 3; - - return fitness_cell(q, index, x, y) + - fitness_ring(q, index, x, y, 1) - - fitness_ring(q, index, x, y, 2) + - fitness_ring(q, index, x, y, 3); -} - -/* Compute a fitness score for the currently configured perspective - * transform, using the features we expect to find by scanning the - * grid. - */ -static int fitness_all(const struct quirc *q, int index) -{ - const struct quirc_grid *qr = &q->grids[index]; - int version = (qr->grid_size - 17) / 4; - const struct quirc_version_info *info = &quirc_version_db[version]; - int score = 0; - int i, j; - int ap_count; - - /* Check the timing pattern */ - for (i = 0; i < qr->grid_size - 14; i++) { - int expect = (i & 1) ? 1 : -1; - - score += fitness_cell(q, index, i + 7, 6) * expect; - score += fitness_cell(q, index, 6, i + 7) * expect; - } - - /* Check capstones */ - score += fitness_capstone(q, index, 0, 0); - score += fitness_capstone(q, index, qr->grid_size - 7, 0); - score += fitness_capstone(q, index, 0, qr->grid_size - 7); - - if (version < 0 || version > QUIRC_MAX_VERSION) - return score; - - /* Check alignment patterns */ - ap_count = 0; - while (info->apat[ap_count]) - ap_count++; - - for (i = 1; i + 1 < ap_count; i++) { - score += fitness_apat(q, index, 6, info->apat[i]); - score += fitness_apat(q, index, info->apat[i], 6); - } - - for (i = 1; i < ap_count; i++) - for (j = 1; j < ap_count; j++) - score += fitness_apat(q, index, - info->apat[i], info->apat[j]); - - return score; -} - -static void jiggle_perspective(struct quirc *q, int index) -{ - struct quirc_grid *qr = &q->grids[index]; - int best = fitness_all(q, index); - int pass; - double adjustments[8]; - int i; - - for (i = 0; i < 8; i++) - adjustments[i] = qr->c[i] * 0.02; - - for (pass = 0; pass < 5; pass++) { - for (i = 0; i < 16; i++) { - int j = i >> 1; - int test; - double old = qr->c[j]; - double step = adjustments[j]; - double new; - - if (i & 1) - new = old + step; - else - new = old - step; - - qr->c[j] = new; - test = fitness_all(q, index); - - if (test > best) - best = test; - else - qr->c[j] = old; - } - - for (i = 0; i < 8; i++) - adjustments[i] *= 0.5; - } -} - -/* Once the capstones are in place and an alignment point has been - * chosen, we call this function to set up a grid-reading perspective - * transform. - */ -static void setup_qr_perspective(struct quirc *q, int index) -{ - struct quirc_grid *qr = &q->grids[index]; - struct quirc_point rect[4]; - - /* Set up the perspective map for reading the grid */ - memcpy(&rect[0], &q->capstones[qr->caps[1]].corners[0], - sizeof(rect[0])); - memcpy(&rect[1], &q->capstones[qr->caps[2]].corners[0], - sizeof(rect[0])); - memcpy(&rect[2], &qr->align, sizeof(rect[0])); - memcpy(&rect[3], &q->capstones[qr->caps[0]].corners[0], - sizeof(rect[0])); - perspective_setup(qr->c, rect, qr->grid_size - 7, qr->grid_size - 7); - - jiggle_perspective(q, index); -} - -/* Rotate the capstone with so that corner 0 is the leftmost with respect - * to the given reference line. - */ -static void rotate_capstone(struct quirc_capstone *cap, - const struct quirc_point *h0, - const struct quirc_point *hd) -{ - struct quirc_point copy[4]; - int j; - int best = 0; - int best_score = 0; - - for (j = 0; j < 4; j++) { - struct quirc_point *p = &cap->corners[j]; - int score = (p->x - h0->x) * -hd->y + - (p->y - h0->y) * hd->x; - - if (!j || score < best_score) { - best = j; - best_score = score; - } - } - - /* Rotate the capstone */ - for (j = 0; j < 4; j++) - memcpy(©[j], &cap->corners[(j + best) % 4], - sizeof(copy[j])); - memcpy(cap->corners, copy, sizeof(cap->corners)); - perspective_setup(cap->c, cap->corners, 7.0, 7.0); -} - -static void record_qr_grid(struct quirc *q, int a, int b, int c) -{ - struct quirc_point h0, hd; - int i; - int qr_index; - struct quirc_grid *qr; - - if (q->num_grids >= QUIRC_MAX_GRIDS) - return; - - /* Construct the hypotenuse line from A to C. B should be to - * the left of this line. - */ - memcpy(&h0, &q->capstones[a].center, sizeof(h0)); - hd.x = q->capstones[c].center.x - q->capstones[a].center.x; - hd.y = q->capstones[c].center.y - q->capstones[a].center.y; - - /* Make sure A-B-C is clockwise */ - if ((q->capstones[b].center.x - h0.x) * -hd.y + - (q->capstones[b].center.y - h0.y) * hd.x > 0) { - int swap = a; - - a = c; - c = swap; - hd.x = -hd.x; - hd.y = -hd.y; - } - - /* Record the grid and its components */ - qr_index = q->num_grids; - qr = &q->grids[q->num_grids++]; - - memset(qr, 0, sizeof(*qr)); - qr->caps[0] = a; - qr->caps[1] = b; - qr->caps[2] = c; - qr->align_region = -1; - - /* Rotate each capstone so that corner 0 is top-left with respect - * to the grid. - */ - for (i = 0; i < 3; i++) { - struct quirc_capstone *cap = &q->capstones[qr->caps[i]]; - - rotate_capstone(cap, &h0, &hd); - cap->qr_grid = qr_index; - } - - /* Check the timing pattern. This doesn't require a perspective - * transform. - */ - if (measure_timing_pattern(q, qr_index) < 0) - goto fail; - - /* Make an estimate based for the alignment pattern based on extending - * lines from capstones A and C. - */ - if (!line_intersect(&q->capstones[a].corners[0], - &q->capstones[a].corners[1], - &q->capstones[c].corners[0], - &q->capstones[c].corners[3], - &qr->align)) - goto fail; - - /* On V2+ grids, we should use the alignment pattern. */ - if (qr->grid_size > 21) { - /* Try to find the actual location of the alignment pattern. */ - find_alignment_pattern(q, qr_index); - - /* Find the point of the alignment pattern closest to the - * top-left of the QR grid. - */ - if (qr->align_region >= 0) { - struct polygon_score_data psd; - struct quirc_region *reg = - &q->regions[qr->align_region]; - - /* Start from some point inside the alignment pattern */ - memcpy(&qr->align, ®->seed, sizeof(qr->align)); - - memcpy(&psd.ref, &hd, sizeof(psd.ref)); - psd.corners = &qr->align; - psd.scores[0] = -hd.y * qr->align.x + - hd.x * qr->align.y; - - flood_fill_seed(q, reg->seed.x, reg->seed.y, - qr->align_region, QUIRC_PIXEL_BLACK, - NULL, NULL); - flood_fill_seed(q, reg->seed.x, reg->seed.y, - QUIRC_PIXEL_BLACK, qr->align_region, - find_leftmost_to_line, &psd); - } - } - - setup_qr_perspective(q, qr_index); - return; - -fail: - /* We've been unable to complete setup for this grid. Undo what we've - * recorded and pretend it never happened. - */ - for (i = 0; i < 3; i++) - q->capstones[qr->caps[i]].qr_grid = -1; - q->num_grids--; -} - -struct neighbour { - int index; - double distance; -}; - -struct neighbour_list { - struct neighbour n[QUIRC_MAX_CAPSTONES]; - int count; -}; - -static void test_neighbours(struct quirc *q, int i, - const struct neighbour_list *hlist, - const struct neighbour_list *vlist) -{ - int j, k; - double best_score = 0.0; - int best_h = -1, best_v = -1; - - /* Test each possible grouping */ - for (j = 0; j < hlist->count; j++) - for (k = 0; k < vlist->count; k++) { - const struct neighbour *hn = &hlist->n[j]; - const struct neighbour *vn = &vlist->n[k]; - double score = fabs(1.0 - hn->distance / vn->distance); - - if (score > 2.5) - continue; - - if (best_h < 0 || score < best_score) { - best_h = hn->index; - best_v = vn->index; - best_score = score; - } - } - - if (best_h < 0 || best_v < 0) - return; - - record_qr_grid(q, best_h, i, best_v); -} - -static void test_grouping(struct quirc *q, int i) -{ - struct quirc_capstone *c1 = &q->capstones[i]; - int j; - struct neighbour_list hlist; - struct neighbour_list vlist; - - if (c1->qr_grid >= 0) - return; - - hlist.count = 0; - vlist.count = 0; - - /* Look for potential neighbours by examining the relative gradients - * from this capstone to others. - */ - for (j = 0; j < q->num_capstones; j++) { - struct quirc_capstone *c2 = &q->capstones[j]; - double u, v; - - if (i == j || c2->qr_grid >= 0) - continue; - - perspective_unmap(c1->c, &c2->center, &u, &v); - - u = fabs(u - 3.5); - v = fabs(v - 3.5); - - if (u < 0.2 * v) { - struct neighbour *n = &hlist.n[hlist.count++]; - - n->index = j; - n->distance = v; - } - - if (v < 0.2 * u) { - struct neighbour *n = &vlist.n[vlist.count++]; - - n->index = j; - n->distance = u; - } - } - - if (!(hlist.count && vlist.count)) - return; - - test_neighbours(q, i, &hlist, &vlist); -} - -static void pixels_setup(struct quirc *q) -{ - if (sizeof(*q->image) == sizeof(*q->pixels)) { - q->pixels = (quirc_pixel_t *)q->image; - } else { - int x, y; - for (y = 0; y < q->h; y++) { - for (x = 0; x < q->w; x++) { - q->pixels[y * q->w + x] = q->image[y * q->w + x]; - } - } - } -} - -uint8_t *quirc_begin(struct quirc *q, int *w, int *h) -{ - q->num_regions = QUIRC_PIXEL_REGION; - q->num_capstones = 0; - q->num_grids = 0; - - if (w) - *w = q->w; - if (h) - *h = q->h; - - return q->image; -} - -void quirc_end(struct quirc *q) -{ - int i; - - pixels_setup(q); - threshold(q); - - for (i = 0; i < q->h; i++) - finder_scan(q, i); - - for (i = 0; i < q->num_capstones; i++) - test_grouping(q, i); -} - -void quirc_extract(const struct quirc *q, int index, - struct quirc_code *code) -{ - const struct quirc_grid *qr = &q->grids[index]; - int y; - int i = 0; - - if (index < 0 || index > q->num_grids) - return; - - memset(code, 0, sizeof(*code)); - - perspective_map(qr->c, 0.0, 0.0, &code->corners[0]); - perspective_map(qr->c, qr->grid_size, 0.0, &code->corners[1]); - perspective_map(qr->c, qr->grid_size, qr->grid_size, - &code->corners[2]); - perspective_map(qr->c, 0.0, qr->grid_size, &code->corners[3]); - - code->size = qr->grid_size; - - for (y = 0; y < qr->grid_size; y++) { - int x; - - for (x = 0; x < qr->grid_size; x++) { - if (read_cell(q, index, x, y) > 0) - code->cell_bitmap[i >> 3] |= (1 << (i & 7)); - - i++; - } - } -} diff --git a/source/libs/quirc/quirc.c b/source/libs/quirc/quirc.c deleted file mode 100644 index 62bc285..0000000 --- a/source/libs/quirc/quirc.c +++ /dev/null @@ -1,91 +0,0 @@ -/* quirc -- QR-code recognition library - * Copyright (C) 2010-2012 Daniel Beer - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include -#include "quirc_internal.h" - -const char *quirc_version(void) -{ - return "1.0"; -} - -struct quirc *quirc_new(void) -{ - struct quirc *q = malloc(sizeof(*q)); - - if (!q) - return NULL; - - memset(q, 0, sizeof(*q)); - return q; -} - -void quirc_destroy(struct quirc *q) -{ - if (q->image) - free(q->image); - if (sizeof(*q->image) != sizeof(*q->pixels)) - free(q->pixels); - - free(q); -} - -int quirc_resize(struct quirc *q, int w, int h) -{ - uint8_t *new_image = realloc(q->image, w * h); - - if (!new_image) - return -1; - - if (sizeof(*q->image) != sizeof(*q->pixels)) { - size_t new_size = w * h * sizeof(quirc_pixel_t); - quirc_pixel_t *new_pixels = realloc(q->pixels, new_size); - if (!new_pixels) - return -1; - q->pixels = new_pixels; - } - - q->image = new_image; - q->w = w; - q->h = h; - - return 0; -} - -int quirc_count(const struct quirc *q) -{ - return q->num_grids; -} - -static const char *const error_table[] = { - [QUIRC_SUCCESS] = "Success", - [QUIRC_ERROR_INVALID_GRID_SIZE] = "Invalid grid size", - [QUIRC_ERROR_INVALID_VERSION] = "Invalid version", - [QUIRC_ERROR_FORMAT_ECC] = "Format data ECC failure", - [QUIRC_ERROR_DATA_ECC] = "ECC failure", - [QUIRC_ERROR_UNKNOWN_DATA_TYPE] = "Unknown data type", - [QUIRC_ERROR_DATA_OVERFLOW] = "Data overflow", - [QUIRC_ERROR_DATA_UNDERFLOW] = "Data underflow" -}; - -const char *quirc_strerror(quirc_decode_error_t err) -{ - if (err >= 0 && err < sizeof(error_table) / sizeof(error_table[0])) - return error_table[err]; - - return "Unknown error"; -} diff --git a/source/libs/quirc/quirc.h b/source/libs/quirc/quirc.h deleted file mode 100644 index 30b8a70..0000000 --- a/source/libs/quirc/quirc.h +++ /dev/null @@ -1,165 +0,0 @@ -/* quirc -- QR-code recognition library - * Copyright (C) 2010-2012 Daniel Beer - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef QUIRC_H_ -#define QUIRC_H_ - -#include - -struct quirc; - -/* Obtain the library version string. */ -const char *quirc_version(void); - -/* Construct a new QR-code recognizer. This function will return NULL - * if sufficient memory could not be allocated. - */ -struct quirc *quirc_new(void); - -/* Destroy a QR-code recognizer. */ -void quirc_destroy(struct quirc *q); - -/* Resize the QR-code recognizer. The size of an image must be - * specified before codes can be analyzed. - * - * This function returns 0 on success, or -1 if sufficient memory could - * not be allocated. - */ -int quirc_resize(struct quirc *q, int w, int h); - -/* These functions are used to process images for QR-code recognition. - * quirc_begin() must first be called to obtain access to a buffer into - * which the input image should be placed. Optionally, the current - * width and height may be returned. - * - * After filling the buffer, quirc_end() should be called to process - * the image for QR-code recognition. The locations and content of each - * code may be obtained using accessor functions described below. - */ -uint8_t *quirc_begin(struct quirc *q, int *w, int *h); -void quirc_end(struct quirc *q); - -/* This structure describes a location in the input image buffer. */ -struct quirc_point { - int x; - int y; -}; - -/* This enum describes the various decoder errors which may occur. */ -typedef enum { - QUIRC_SUCCESS = 0, - QUIRC_ERROR_INVALID_GRID_SIZE, - QUIRC_ERROR_INVALID_VERSION, - QUIRC_ERROR_FORMAT_ECC, - QUIRC_ERROR_DATA_ECC, - QUIRC_ERROR_UNKNOWN_DATA_TYPE, - QUIRC_ERROR_DATA_OVERFLOW, - QUIRC_ERROR_DATA_UNDERFLOW -} quirc_decode_error_t; - -/* Return a string error message for an error code. */ -const char *quirc_strerror(quirc_decode_error_t err); - -/* Limits on the maximum size of QR-codes and their content. */ -#define QUIRC_MAX_BITMAP 3917 -#define QUIRC_MAX_PAYLOAD 8896 - -/* QR-code ECC types. */ -#define QUIRC_ECC_LEVEL_M 0 -#define QUIRC_ECC_LEVEL_L 1 -#define QUIRC_ECC_LEVEL_H 2 -#define QUIRC_ECC_LEVEL_Q 3 - -/* QR-code data types. */ -#define QUIRC_DATA_TYPE_NUMERIC 1 -#define QUIRC_DATA_TYPE_ALPHA 2 -#define QUIRC_DATA_TYPE_BYTE 4 -#define QUIRC_DATA_TYPE_KANJI 8 - -/* Common character encodings */ -#define QUIRC_ECI_ISO_8859_1 1 -#define QUIRC_ECI_IBM437 2 -#define QUIRC_ECI_ISO_8859_2 4 -#define QUIRC_ECI_ISO_8859_3 5 -#define QUIRC_ECI_ISO_8859_4 6 -#define QUIRC_ECI_ISO_8859_5 7 -#define QUIRC_ECI_ISO_8859_6 8 -#define QUIRC_ECI_ISO_8859_7 9 -#define QUIRC_ECI_ISO_8859_8 10 -#define QUIRC_ECI_ISO_8859_9 11 -#define QUIRC_ECI_WINDOWS_874 13 -#define QUIRC_ECI_ISO_8859_13 15 -#define QUIRC_ECI_ISO_8859_15 17 -#define QUIRC_ECI_SHIFT_JIS 20 -#define QUIRC_ECI_UTF_8 26 - -/* This structure is used to return information about detected QR codes - * in the input image. - */ -struct quirc_code { - /* The four corners of the QR-code, from top left, clockwise */ - struct quirc_point corners[4]; - - /* The number of cells across in the QR-code. The cell bitmap - * is a bitmask giving the actual values of cells. If the cell - * at (x, y) is black, then the following bit is set: - * - * cell_bitmap[i << 3] & (1 << (i & 7)) - * - * where i = (y * size) + x. - */ - int size; - uint8_t cell_bitmap[QUIRC_MAX_BITMAP]; -}; - -/* This structure holds the decoded QR-code data */ -struct quirc_data { - /* Various parameters of the QR-code. These can mostly be - * ignored if you only care about the data. - */ - int version; - int ecc_level; - int mask; - - /* This field is the highest-valued data type found in the QR - * code. - */ - int data_type; - - /* Data payload. For the Kanji datatype, payload is encoded as - * Shift-JIS. For all other datatypes, payload is ASCII text. - */ - uint8_t payload[QUIRC_MAX_PAYLOAD]; - int payload_len; - - /* ECI assignment number */ - uint32_t eci; -}; - -/* Return the number of QR-codes identified in the last processed - * image. - */ -int quirc_count(const struct quirc *q); - -/* Extract the QR-code specified by the given index. */ -void quirc_extract(const struct quirc *q, int index, - struct quirc_code *code); - -/* Decode a QR-code, returning the payload data. */ -quirc_decode_error_t quirc_decode(const struct quirc_code *code, - struct quirc_data *data); - -#endif diff --git a/source/libs/quirc/quirc_internal.h b/source/libs/quirc/quirc_internal.h deleted file mode 100644 index a2630e7..0000000 --- a/source/libs/quirc/quirc_internal.h +++ /dev/null @@ -1,114 +0,0 @@ -/* quirc -- QR-code recognition library - * Copyright (C) 2010-2012 Daniel Beer - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef QUIRC_INTERNAL_H_ -#define QUIRC_INTERNAL_H_ - -#include "quirc.h" - -#define QUIRC_PIXEL_WHITE 0 -#define QUIRC_PIXEL_BLACK 1 -#define QUIRC_PIXEL_REGION 2 - -#ifndef QUIRC_MAX_REGIONS -#define QUIRC_MAX_REGIONS 254 -#endif -#define QUIRC_MAX_CAPSTONES 32 -#define QUIRC_MAX_GRIDS 8 - -#define QUIRC_PERSPECTIVE_PARAMS 8 - -#if QUIRC_MAX_REGIONS < UINT8_MAX -typedef uint8_t quirc_pixel_t; -#elif QUIRC_MAX_REGIONS < UINT16_MAX -typedef uint16_t quirc_pixel_t; -#else -#error "QUIRC_MAX_REGIONS > 65534 is not supported" -#endif - -struct quirc_region { - struct quirc_point seed; - int count; - int capstone; -}; - -struct quirc_capstone { - int ring; - int stone; - - struct quirc_point corners[4]; - struct quirc_point center; - double c[QUIRC_PERSPECTIVE_PARAMS]; - - int qr_grid; -}; - -struct quirc_grid { - /* Capstone indices */ - int caps[3]; - - /* Alignment pattern region and corner */ - int align_region; - struct quirc_point align; - - /* Timing pattern endpoints */ - struct quirc_point tpep[3]; - int hscan; - int vscan; - - /* Grid size and perspective transform */ - int grid_size; - double c[QUIRC_PERSPECTIVE_PARAMS]; -}; - -struct quirc { - uint8_t *image; - quirc_pixel_t *pixels; - int w; - int h; - - int num_regions; - struct quirc_region regions[QUIRC_MAX_REGIONS]; - - int num_capstones; - struct quirc_capstone capstones[QUIRC_MAX_CAPSTONES]; - - int num_grids; - struct quirc_grid grids[QUIRC_MAX_GRIDS]; -}; - -/************************************************************************ - * QR-code version information database - */ - -#define QUIRC_MAX_VERSION 40 -#define QUIRC_MAX_ALIGNMENT 7 - -struct quirc_rs_params { - int bs; /* Block size */ - int dw; /* Data words */ - int ce; /* Correctable errors */ -}; - -struct quirc_version_info { - int data_bytes; - int apat[QUIRC_MAX_ALIGNMENT]; - struct quirc_rs_params ecc[4]; -}; - -extern const struct quirc_version_info quirc_version_db[QUIRC_MAX_VERSION + 1]; - -#endif diff --git a/source/libs/quirc/version_db.c b/source/libs/quirc/version_db.c deleted file mode 100644 index 693d413..0000000 --- a/source/libs/quirc/version_db.c +++ /dev/null @@ -1,421 +0,0 @@ -/* quirc -- QR-code recognition library - * Copyright (C) 2010-2012 Daniel Beer - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "quirc_internal.h" - -const struct quirc_version_info quirc_version_db[QUIRC_MAX_VERSION + 1] = { - {0}, - { /* Version 1 */ - .data_bytes = 26, - .apat = {0}, - .ecc = { - {.bs = 26, .dw = 16, .ce = 4}, - {.bs = 26, .dw = 19, .ce = 2}, - {.bs = 26, .dw = 9, .ce = 8}, - {.bs = 26, .dw = 13, .ce = 6} - } - }, - { /* Version 2 */ - .data_bytes = 44, - .apat = {6, 18, 0}, - .ecc = { - {.bs = 44, .dw = 28, .ce = 8}, - {.bs = 44, .dw = 34, .ce = 4}, - {.bs = 44, .dw = 16, .ce = 14}, - {.bs = 44, .dw = 22, .ce = 11} - } - }, - { /* Version 3 */ - .data_bytes = 70, - .apat = {6, 22, 0}, - .ecc = { - {.bs = 70, .dw = 44, .ce = 13}, - {.bs = 70, .dw = 55, .ce = 7}, - {.bs = 35, .dw = 13, .ce = 11}, - {.bs = 35, .dw = 17, .ce = 9} - } - }, - { /* Version 4 */ - .data_bytes = 100, - .apat = {6, 26, 0}, - .ecc = { - {.bs = 50, .dw = 32, .ce = 9}, - {.bs = 100, .dw = 80, .ce = 10}, - {.bs = 25, .dw = 9, .ce = 8}, - {.bs = 50, .dw = 24, .ce = 13} - } - }, - { /* Version 5 */ - .data_bytes = 134, - .apat = {6, 30, 0}, - .ecc = { - {.bs = 67, .dw = 43, .ce = 12}, - {.bs = 134, .dw = 108, .ce = 13}, - {.bs = 33, .dw = 11, .ce = 11}, - {.bs = 33, .dw = 15, .ce = 9} - } - }, - { /* Version 6 */ - .data_bytes = 172, - .apat = {6, 34, 0}, - .ecc = { - {.bs = 43, .dw = 27, .ce = 8}, - {.bs = 86, .dw = 68, .ce = 9}, - {.bs = 43, .dw = 15, .ce = 14}, - {.bs = 43, .dw = 19, .ce = 12} - } - }, - { /* Version 7 */ - .data_bytes = 196, - .apat = {6, 22, 38, 0}, - .ecc = { - {.bs = 49, .dw = 31, .ce = 9}, - {.bs = 98, .dw = 78, .ce = 10}, - {.bs = 39, .dw = 13, .ce = 13}, - {.bs = 32, .dw = 14, .ce = 9} - } - }, - { /* Version 8 */ - .data_bytes = 242, - .apat = {6, 24, 42, 0}, - .ecc = { - {.bs = 60, .dw = 38, .ce = 11}, - {.bs = 121, .dw = 97, .ce = 12}, - {.bs = 40, .dw = 14, .ce = 13}, - {.bs = 40, .dw = 18, .ce = 11} - } - }, - { /* Version 9 */ - .data_bytes = 292, - .apat = {6, 26, 46, 0}, - .ecc = { - {.bs = 58, .dw = 36, .ce = 11}, - {.bs = 146, .dw = 116, .ce = 15}, - {.bs = 36, .dw = 12, .ce = 12}, - {.bs = 36, .dw = 16, .ce = 10} - } - }, - { /* Version 10 */ - .data_bytes = 346, - .apat = {6, 28, 50, 0}, - .ecc = { - {.bs = 69, .dw = 43, .ce = 13}, - {.bs = 86, .dw = 68, .ce = 9}, - {.bs = 43, .dw = 15, .ce = 14}, - {.bs = 43, .dw = 19, .ce = 12} - } - }, - { /* Version 11 */ - .data_bytes = 404, - .apat = {6, 30, 54, 0}, - .ecc = { - {.bs = 80, .dw = 50, .ce = 15}, - {.bs = 101, .dw = 81, .ce = 10}, - {.bs = 36, .dw = 12, .ce = 12}, - {.bs = 50, .dw = 22, .ce = 14} - } - }, - { /* Version 12 */ - .data_bytes = 466, - .apat = {6, 32, 58, 0}, - .ecc = { - {.bs = 58, .dw = 36, .ce = 11}, - {.bs = 116, .dw = 92, .ce = 12}, - {.bs = 42, .dw = 14, .ce = 14}, - {.bs = 46, .dw = 20, .ce = 14} - } - }, - { /* Version 13 */ - .data_bytes = 532, - .apat = {6, 34, 62, 0}, - .ecc = { - {.bs = 59, .dw = 37, .ce = 11}, - {.bs = 133, .dw = 107, .ce = 13}, - {.bs = 33, .dw = 11, .ce = 11}, - {.bs = 44, .dw = 20, .ce = 12} - } - }, - { /* Version 14 */ - .data_bytes = 581, - .apat = {6, 26, 46, 66, 0}, - .ecc = { - {.bs = 65, .dw = 41, .ce = 12}, - {.bs = 109, .dw = 87, .ce = 11}, - {.bs = 36, .dw = 12, .ce = 12}, - {.bs = 54, .dw = 24, .ce = 15} - } - }, - { /* Version 15 */ - .data_bytes = 655, - .apat = {6, 26, 48, 70, 0}, - .ecc = { - {.bs = 65, .dw = 41, .ce = 12}, - {.bs = 109, .dw = 87, .ce = 11}, - {.bs = 36, .dw = 12, .ce = 12}, - {.bs = 54, .dw = 24, .ce = 15} - } - }, - { /* Version 16 */ - .data_bytes = 733, - .apat = {6, 26, 50, 74, 0}, - .ecc = { - {.bs = 73, .dw = 45, .ce = 14}, - {.bs = 122, .dw = 98, .ce = 12}, - {.bs = 45, .dw = 15, .ce = 15}, - {.bs = 43, .dw = 19, .ce = 12} - } - }, - { /* Version 17 */ - .data_bytes = 815, - .apat = {6, 30, 54, 78, 0}, - .ecc = { - {.bs = 74, .dw = 46, .ce = 14}, - {.bs = 135, .dw = 107, .ce = 14}, - {.bs = 42, .dw = 14, .ce = 14}, - {.bs = 50, .dw = 22, .ce = 14} - } - }, - { /* Version 18 */ - .data_bytes = 901, - .apat = {6, 30, 56, 82, 0}, - .ecc = { - {.bs = 69, .dw = 43, .ce = 13}, - {.bs = 150, .dw = 120, .ce = 15}, - {.bs = 42, .dw = 14, .ce = 14}, - {.bs = 50, .dw = 22, .ce = 14} - } - }, - { /* Version 19 */ - .data_bytes = 991, - .apat = {6, 30, 58, 86, 0}, - .ecc = { - {.bs = 70, .dw = 44, .ce = 13}, - {.bs = 141, .dw = 113, .ce = 14}, - {.bs = 39, .dw = 13, .ce = 13}, - {.bs = 47, .dw = 21, .ce = 13} - } - }, - { /* Version 20 */ - .data_bytes = 1085, - .apat = {6, 34, 62, 90, 0}, - .ecc = { - {.bs = 67, .dw = 41, .ce = 13}, - {.bs = 135, .dw = 107, .ce = 14}, - {.bs = 43, .dw = 15, .ce = 14}, - {.bs = 54, .dw = 24, .ce = 15} - } - }, - { /* Version 21 */ - .data_bytes = 1156, - .apat = {6, 28, 50, 72, 92, 0}, - .ecc = { - {.bs = 68, .dw = 42, .ce = 13}, - {.bs = 144, .dw = 116, .ce = 14}, - {.bs = 46, .dw = 16, .ce = 15}, - {.bs = 50, .dw = 22, .ce = 14} - } - }, - { /* Version 22 */ - .data_bytes = 1258, - .apat = {6, 26, 50, 74, 98, 0}, - .ecc = { - {.bs = 74, .dw = 46, .ce = 14}, - {.bs = 139, .dw = 111, .ce = 14}, - {.bs = 37, .dw = 13, .ce = 12}, - {.bs = 54, .dw = 24, .ce = 15} - } - }, - { /* Version 23 */ - .data_bytes = 1364, - .apat = {6, 30, 54, 78, 102, 0}, - .ecc = { - {.bs = 75, .dw = 47, .ce = 14}, - {.bs = 151, .dw = 121, .ce = 15}, - {.bs = 45, .dw = 15, .ce = 15}, - {.bs = 54, .dw = 24, .ce = 15} - } - }, - { /* Version 24 */ - .data_bytes = 1474, - .apat = {6, 28, 54, 80, 106, 0}, - .ecc = { - {.bs = 73, .dw = 45, .ce = 14}, - {.bs = 147, .dw = 117, .ce = 15}, - {.bs = 46, .dw = 16, .ce = 15}, - {.bs = 54, .dw = 24, .ce = 15} - } - }, - { /* Version 25 */ - .data_bytes = 1588, - .apat = {6, 32, 58, 84, 110, 0}, - .ecc = { - {.bs = 75, .dw = 47, .ce = 14}, - {.bs = 132, .dw = 106, .ce = 13}, - {.bs = 45, .dw = 15, .ce = 15}, - {.bs = 54, .dw = 24, .ce = 15} - } - }, - { /* Version 26 */ - .data_bytes = 1706, - .apat = {6, 30, 58, 86, 114, 0}, - .ecc = { - {.bs = 74, .dw = 46, .ce = 14}, - {.bs = 142, .dw = 114, .ce = 14}, - {.bs = 46, .dw = 16, .ce = 15}, - {.bs = 50, .dw = 22, .ce = 14} - } - }, - { /* Version 27 */ - .data_bytes = 1828, - .apat = {6, 34, 62, 90, 118, 0}, - .ecc = { - {.bs = 73, .dw = 45, .ce = 14}, - {.bs = 152, .dw = 122, .ce = 15}, - {.bs = 45, .dw = 15, .ce = 15}, - {.bs = 53, .dw = 23, .ce = 15} - } - }, - { /* Version 28 */ - .data_bytes = 1921, - .apat = {6, 26, 50, 74, 98, 122, 0}, - .ecc = { - {.bs = 73, .dw = 45, .ce = 14}, - {.bs = 147, .dw = 117, .ce = 15}, - {.bs = 45, .dw = 15, .ce = 15}, - {.bs = 54, .dw = 24, .ce = 15} - } - }, - { /* Version 29 */ - .data_bytes = 2051, - .apat = {6, 30, 54, 78, 102, 126, 0}, - .ecc = { - {.bs = 73, .dw = 45, .ce = 14}, - {.bs = 146, .dw = 116, .ce = 15}, - {.bs = 45, .dw = 15, .ce = 15}, - {.bs = 73, .dw = 45, .ce = 14} - } - }, - { /* Version 30 */ - .data_bytes = 2185, - .apat = {6, 26, 52, 78, 104, 130, 0}, - .ecc = { - {.bs = 75, .dw = 47, .ce = 14}, - {.bs = 145, .dw = 115, .ce = 15}, - {.bs = 45, .dw = 15, .ce = 15}, - {.bs = 54, .dw = 24, .ce = 15} - } - }, - { /* Version 31 */ - .data_bytes = 2323, - .apat = {6, 30, 56, 82, 108, 134, 0}, - .ecc = { - {.bs = 74, .dw = 46, .ce = 14}, - {.bs = 145, .dw = 115, .ce = 15}, - {.bs = 45, .dw = 15, .ce = 15}, - {.bs = 54, .dw = 24, .ce = 15} - } - }, - { /* Version 32 */ - .data_bytes = 2465, - .apat = {6, 34, 60, 86, 112, 138, 0}, - .ecc = { - {.bs = 74, .dw = 46, .ce = 14}, - {.bs = 145, .dw = 115, .ce = 15}, - {.bs = 45, .dw = 15, .ce = 15}, - {.bs = 54, .dw = 24, .ce = 15} - } - }, - { /* Version 33 */ - .data_bytes = 2611, - .apat = {6, 30, 58, 96, 114, 142, 0}, - .ecc = { - {.bs = 74, .dw = 46, .ce = 14}, - {.bs = 145, .dw = 115, .ce = 15}, - {.bs = 45, .dw = 15, .ce = 15}, - {.bs = 54, .dw = 24, .ce = 15} - } - }, - { /* Version 34 */ - .data_bytes = 2761, - .apat = {6, 34, 62, 90, 118, 146, 0}, - .ecc = { - {.bs = 74, .dw = 46, .ce = 14}, - {.bs = 145, .dw = 115, .ce = 15}, - {.bs = 46, .dw = 16, .ce = 15}, - {.bs = 54, .dw = 24, .ce = 15} - } - }, - { /* Version 35 */ - .data_bytes = 2876, - .apat = {6, 30, 54, 78, 102, 126, 150}, - .ecc = { - {.bs = 75, .dw = 47, .ce = 14}, - {.bs = 151, .dw = 121, .ce = 15}, - {.bs = 45, .dw = 15, .ce = 15}, - {.bs = 54, .dw = 24, .ce = 15} - } - }, - { /* Version 36 */ - .data_bytes = 3034, - .apat = {6, 24, 50, 76, 102, 128, 154}, - .ecc = { - {.bs = 75, .dw = 47, .ce = 14}, - {.bs = 151, .dw = 121, .ce = 15}, - {.bs = 45, .dw = 15, .ce = 15}, - {.bs = 54, .dw = 24, .ce = 15} - } - }, - { /* Version 37 */ - .data_bytes = 3196, - .apat = {6, 28, 54, 80, 106, 132, 158}, - .ecc = { - {.bs = 74, .dw = 46, .ce = 14}, - {.bs = 152, .dw = 122, .ce = 15}, - {.bs = 45, .dw = 15, .ce = 15}, - {.bs = 54, .dw = 24, .ce = 15} - } - }, - { /* Version 38 */ - .data_bytes = 3362, - .apat = {6, 32, 58, 84, 110, 136, 162}, - .ecc = { - {.bs = 74, .dw = 46, .ce = 14}, - {.bs = 152, .dw = 122, .ce = 15}, - {.bs = 45, .dw = 15, .ce = 15}, - {.bs = 54, .dw = 24, .ce = 15} - } - }, - { /* Version 39 */ - .data_bytes = 3532, - .apat = {6, 26, 54, 82, 110, 138, 166}, - .ecc = { - {.bs = 75, .dw = 47, .ce = 14}, - {.bs = 147, .dw = 117, .ce = 15}, - {.bs = 45, .dw = 15, .ce = 15}, - {.bs = 54, .dw = 24, .ce = 15} - } - }, - { /* Version 40 */ - .data_bytes = 3706, - .apat = {6, 30, 58, 86, 114, 142, 170}, - .ecc = { - {.bs = 75, .dw = 47, .ce = 14}, - {.bs = 148, .dw = 118, .ce = 15}, - {.bs = 45, .dw = 15, .ce = 15}, - {.bs = 54, .dw = 24, .ce = 15} - } - } -}; diff --git a/source/libs/stb_image/stb_image.c b/source/libs/stb_image/stb_image.c deleted file mode 100644 index badb3ef..0000000 --- a/source/libs/stb_image/stb_image.c +++ /dev/null @@ -1,2 +0,0 @@ -#define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" \ No newline at end of file diff --git a/source/libs/stb_image/stb_image.h b/source/libs/stb_image/stb_image.h deleted file mode 100644 index d9c21bc..0000000 --- a/source/libs/stb_image/stb_image.h +++ /dev/null @@ -1,7462 +0,0 @@ -/* stb_image - v2.19 - public domain image loader - http://nothings.org/stb - no warranty implied; use at your own risk - - Do this: - #define STB_IMAGE_IMPLEMENTATION - before you include this file in *one* C or C++ file to create the implementation. - - // i.e. it should look like this: - #include ... - #include ... - #include ... - #define STB_IMAGE_IMPLEMENTATION - #include "stb_image.h" - - You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. - And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free - - - QUICK NOTES: - Primarily of interest to game developers and other people who can - avoid problematic images and only need the trivial interface - - JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) - PNG 1/2/4/8/16-bit-per-channel - - TGA (not sure what subset, if a subset) - BMP non-1bpp, non-RLE - PSD (composited view only, no extra channels, 8/16 bit-per-channel) - - GIF (*comp always reports as 4-channel) - HDR (radiance rgbE format) - PIC (Softimage PIC) - PNM (PPM and PGM binary only) - - Animated GIF still needs a proper API, but here's one way to do it: - http://gist.github.com/urraka/685d9a6340b26b830d49 - - - decode from memory or through FILE (define STBI_NO_STDIO to remove code) - - decode from arbitrary I/O callbacks - - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) - - Full documentation under "DOCUMENTATION" below. - - -LICENSE - - See end of file for license information. - -RECENT REVISION HISTORY: - - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings - 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes - 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 - RGB-format JPEG; remove white matting in PSD; - allocate large structures on the stack; - correct channel count for PNG & BMP - 2.10 (2016-01-22) avoid warning introduced in 2.09 - 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED - - See end of file for full revision history. - - - ============================ Contributors ========================= - - Image formats Extensions, features - Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) - Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) - Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) - Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) - Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) - Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) - Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) - github:urraka (animated gif) Junggon Kim (PNM comments) - Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) - socks-the-fox (16-bit PNG) - Jeremy Sawicki (handle all ImageNet JPGs) - Optimizations & bugfixes Mikhail Morozov (1-bit BMP) - Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) - Arseny Kapoulkine - John-Mark Allen - - Bug & warning fixes - Marc LeBlanc David Woo Guillaume George Martins Mozeiko - Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan - Dave Moore Roy Eltham Hayaki Saito Nathan Reed - Won Chun Luke Graham Johan Duparc Nick Verigakis - the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh - Janez Zemva John Bartholomew Michal Cichon github:romigrou - Jonathan Blow Ken Hamada Tero Hanninen github:svdijk - Laurent Gomila Cort Stratton Sergio Gonzalez github:snagar - Aruelien Pocheville Thibault Reuille Cass Everitt github:Zelex - Ryamond Barbiero Paul Du Bois Engin Manap github:grim210 - Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw - Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus - Julian Raschke Gregory Mullen Baldur Karlsson github:poppolopoppo - Christian Floisand Kevin Schmidt github:darealshinji - Blazej Dariusz Roszkowski github:Michaelangel007 -*/ - -#ifndef STBI_INCLUDE_STB_IMAGE_H -#define STBI_INCLUDE_STB_IMAGE_H - -// DOCUMENTATION -// -// Limitations: -// - no 12-bit-per-channel JPEG -// - no JPEGs with arithmetic coding -// - GIF always returns *comp=4 -// -// Basic usage (see HDR discussion below for HDR usage): -// int x,y,n; -// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); -// // ... process data if not NULL ... -// // ... x = width, y = height, n = # 8-bit components per pixel ... -// // ... replace '0' with '1'..'4' to force that many components per pixel -// // ... but 'n' will always be the number that it would have been if you said 0 -// stbi_image_free(data) -// -// Standard parameters: -// int *x -- outputs image width in pixels -// int *y -- outputs image height in pixels -// int *channels_in_file -- outputs # of image components in image file -// int desired_channels -- if non-zero, # of image components requested in result -// -// The return value from an image loader is an 'unsigned char *' which points -// to the pixel data, or NULL on an allocation failure or if the image is -// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, -// with each pixel consisting of N interleaved 8-bit components; the first -// pixel pointed to is top-left-most in the image. There is no padding between -// image scanlines or between pixels, regardless of format. The number of -// components N is 'desired_channels' if desired_channels is non-zero, or -// *channels_in_file otherwise. If desired_channels is non-zero, -// *channels_in_file has the number of components that _would_ have been -// output otherwise. E.g. if you set desired_channels to 4, you will always -// get RGBA output, but you can check *channels_in_file to see if it's trivially -// opaque because e.g. there were only 3 channels in the source image. -// -// An output image with N components has the following components interleaved -// in this order in each pixel: -// -// N=#comp components -// 1 grey -// 2 grey, alpha -// 3 red, green, blue -// 4 red, green, blue, alpha -// -// If image loading fails for any reason, the return value will be NULL, -// and *x, *y, *channels_in_file will be unchanged. The function -// stbi_failure_reason() can be queried for an extremely brief, end-user -// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS -// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly -// more user-friendly ones. -// -// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. -// -// =========================================================================== -// -// Philosophy -// -// stb libraries are designed with the following priorities: -// -// 1. easy to use -// 2. easy to maintain -// 3. good performance -// -// Sometimes I let "good performance" creep up in priority over "easy to maintain", -// and for best performance I may provide less-easy-to-use APIs that give higher -// performance, in addition to the easy to use ones. Nevertheless, it's important -// to keep in mind that from the standpoint of you, a client of this library, -// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. -// -// Some secondary priorities arise directly from the first two, some of which -// make more explicit reasons why performance can't be emphasized. -// -// - Portable ("ease of use") -// - Small source code footprint ("easy to maintain") -// - No dependencies ("ease of use") -// -// =========================================================================== -// -// I/O callbacks -// -// I/O callbacks allow you to read from arbitrary sources, like packaged -// files or some other source. Data read from callbacks are processed -// through a small internal buffer (currently 128 bytes) to try to reduce -// overhead. -// -// The three functions you must define are "read" (reads some bytes of data), -// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). -// -// =========================================================================== -// -// SIMD support -// -// The JPEG decoder will try to automatically use SIMD kernels on x86 when -// supported by the compiler. For ARM Neon support, you must explicitly -// request it. -// -// (The old do-it-yourself SIMD API is no longer supported in the current -// code.) -// -// On x86, SSE2 will automatically be used when available based on a run-time -// test; if not, the generic C versions are used as a fall-back. On ARM targets, -// the typical path is to have separate builds for NEON and non-NEON devices -// (at least this is true for iOS and Android). Therefore, the NEON support is -// toggled by a build flag: define STBI_NEON to get NEON loops. -// -// If for some reason you do not want to use any of SIMD code, or if -// you have issues compiling it, you can disable it entirely by -// defining STBI_NO_SIMD. -// -// =========================================================================== -// -// HDR image support (disable by defining STBI_NO_HDR) -// -// stb_image now supports loading HDR images in general, and currently -// the Radiance .HDR file format, although the support is provided -// generically. You can still load any file through the existing interface; -// if you attempt to load an HDR file, it will be automatically remapped to -// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; -// both of these constants can be reconfigured through this interface: -// -// stbi_hdr_to_ldr_gamma(2.2f); -// stbi_hdr_to_ldr_scale(1.0f); -// -// (note, do not use _inverse_ constants; stbi_image will invert them -// appropriately). -// -// Additionally, there is a new, parallel interface for loading files as -// (linear) floats to preserve the full dynamic range: -// -// float *data = stbi_loadf(filename, &x, &y, &n, 0); -// -// If you load LDR images through this interface, those images will -// be promoted to floating point values, run through the inverse of -// constants corresponding to the above: -// -// stbi_ldr_to_hdr_scale(1.0f); -// stbi_ldr_to_hdr_gamma(2.2f); -// -// Finally, given a filename (or an open file or memory block--see header -// file for details) containing image data, you can query for the "most -// appropriate" interface to use (that is, whether the image is HDR or -// not), using: -// -// stbi_is_hdr(char *filename); -// -// =========================================================================== -// -// iPhone PNG support: -// -// By default we convert iphone-formatted PNGs back to RGB, even though -// they are internally encoded differently. You can disable this conversion -// by by calling stbi_convert_iphone_png_to_rgb(0), in which case -// you will always just get the native iphone "format" through (which -// is BGR stored in RGB). -// -// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per -// pixel to remove any premultiplied alpha *only* if the image file explicitly -// says there's premultiplied data (currently only happens in iPhone images, -// and only if iPhone convert-to-rgb processing is on). -// -// =========================================================================== -// -// ADDITIONAL CONFIGURATION -// -// - You can suppress implementation of any of the decoders to reduce -// your code footprint by #defining one or more of the following -// symbols before creating the implementation. -// -// STBI_NO_JPEG -// STBI_NO_PNG -// STBI_NO_BMP -// STBI_NO_PSD -// STBI_NO_TGA -// STBI_NO_GIF -// STBI_NO_HDR -// STBI_NO_PIC -// STBI_NO_PNM (.ppm and .pgm) -// -// - You can request *only* certain decoders and suppress all other ones -// (this will be more forward-compatible, as addition of new decoders -// doesn't require you to disable them explicitly): -// -// STBI_ONLY_JPEG -// STBI_ONLY_PNG -// STBI_ONLY_BMP -// STBI_ONLY_PSD -// STBI_ONLY_TGA -// STBI_ONLY_GIF -// STBI_ONLY_HDR -// STBI_ONLY_PIC -// STBI_ONLY_PNM (.ppm and .pgm) -// -// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still -// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB -// - - -#ifndef STBI_NO_STDIO -#include -#endif // STBI_NO_STDIO - -#define STBI_VERSION 1 - -enum -{ - STBI_default = 0, // only used for desired_channels - - STBI_grey = 1, - STBI_grey_alpha = 2, - STBI_rgb = 3, - STBI_rgb_alpha = 4 -}; - -typedef unsigned char stbi_uc; -typedef unsigned short stbi_us; - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef STB_IMAGE_STATIC -#define STBIDEF static -#else -#define STBIDEF extern -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// PRIMARY API - works on images of any type -// - -// -// load image by filename, open file, or memory buffer -// - -typedef struct -{ - int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read - void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative - int (*eof) (void *user); // returns nonzero if we are at end of file/data -} stbi_io_callbacks; - -//////////////////////////////////// -// -// 8-bits-per-channel interface -// - -STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -#endif - - -#ifndef STBI_NO_STDIO -STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); -// for stbi_load_from_file, file pointer is left pointing immediately after image -#endif - -//////////////////////////////////// -// -// 16-bits-per-channel interface -// - -STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); - -#ifndef STBI_NO_STDIO -STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); -#endif - -//////////////////////////////////// -// -// float-per-channel interface -// -#ifndef STBI_NO_LINEAR - STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); - - #ifndef STBI_NO_STDIO - STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); - #endif -#endif - -#ifndef STBI_NO_HDR - STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); - STBIDEF void stbi_hdr_to_ldr_scale(float scale); -#endif // STBI_NO_HDR - -#ifndef STBI_NO_LINEAR - STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); - STBIDEF void stbi_ldr_to_hdr_scale(float scale); -#endif // STBI_NO_LINEAR - -// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename); -STBIDEF int stbi_is_hdr_from_file(FILE *f); -#endif // STBI_NO_STDIO - - -// get a VERY brief reason for failure -// NOT THREADSAFE -STBIDEF const char *stbi_failure_reason (void); - -// free the loaded image -- this is just free() -STBIDEF void stbi_image_free (void *retval_from_stbi_load); - -// get image dimensions & components without fully decoding -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit (char const *filename); -STBIDEF int stbi_is_16_bit_from_file(FILE *f); -#endif - - - -// for image formats that explicitly notate that they have premultiplied alpha, -// we just return the colors as stored in the file. set this flag to force -// unpremultiplication. results are undefined if the unpremultiply overflow. -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); - -// indicate whether we should process iphone images back to canonical format, -// or just pass them through "as-is" -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); - -// flip the image vertically, so the first pixel in the output array is the bottom left -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); - -// ZLIB client - used by PNG, available for other purposes - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); -STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - -STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - - -#ifdef __cplusplus -} -#endif - -// -// -//// end header file ///////////////////////////////////////////////////// -#endif // STBI_INCLUDE_STB_IMAGE_H - -#ifdef STB_IMAGE_IMPLEMENTATION - -#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ - || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ - || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ - || defined(STBI_ONLY_ZLIB) - #ifndef STBI_ONLY_JPEG - #define STBI_NO_JPEG - #endif - #ifndef STBI_ONLY_PNG - #define STBI_NO_PNG - #endif - #ifndef STBI_ONLY_BMP - #define STBI_NO_BMP - #endif - #ifndef STBI_ONLY_PSD - #define STBI_NO_PSD - #endif - #ifndef STBI_ONLY_TGA - #define STBI_NO_TGA - #endif - #ifndef STBI_ONLY_GIF - #define STBI_NO_GIF - #endif - #ifndef STBI_ONLY_HDR - #define STBI_NO_HDR - #endif - #ifndef STBI_ONLY_PIC - #define STBI_NO_PIC - #endif - #ifndef STBI_ONLY_PNM - #define STBI_NO_PNM - #endif -#endif - -#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) -#define STBI_NO_ZLIB -#endif - - -#include -#include // ptrdiff_t on osx -#include -#include -#include - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -#include // ldexp, pow -#endif - -#ifndef STBI_NO_STDIO -#include -#endif - -#ifndef STBI_ASSERT -#include -#define STBI_ASSERT(x) assert(x) -#endif - - -#ifndef _MSC_VER - #ifdef __cplusplus - #define stbi_inline inline - #else - #define stbi_inline - #endif -#else - #define stbi_inline __forceinline -#endif - - -#ifdef _MSC_VER -typedef unsigned short stbi__uint16; -typedef signed short stbi__int16; -typedef unsigned int stbi__uint32; -typedef signed int stbi__int32; -#else -#include -typedef uint16_t stbi__uint16; -typedef int16_t stbi__int16; -typedef uint32_t stbi__uint32; -typedef int32_t stbi__int32; -#endif - -// should produce compiler error if size is wrong -typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; - -#ifdef _MSC_VER -#define STBI_NOTUSED(v) (void)(v) -#else -#define STBI_NOTUSED(v) (void)sizeof(v) -#endif - -#ifdef _MSC_VER -#define STBI_HAS_LROTL -#endif - -#ifdef STBI_HAS_LROTL - #define stbi_lrot(x,y) _lrotl(x,y) -#else - #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) -#endif - -#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) -// ok -#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) -// ok -#else -#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." -#endif - -#ifndef STBI_MALLOC -#define STBI_MALLOC(sz) malloc(sz) -#define STBI_REALLOC(p,newsz) realloc(p,newsz) -#define STBI_FREE(p) free(p) -#endif - -#ifndef STBI_REALLOC_SIZED -#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) -#endif - -// x86/x64 detection -#if defined(__x86_64__) || defined(_M_X64) -#define STBI__X64_TARGET -#elif defined(__i386) || defined(_M_IX86) -#define STBI__X86_TARGET -#endif - -#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) -// gcc doesn't support sse2 intrinsics unless you compile with -msse2, -// which in turn means it gets to use SSE2 everywhere. This is unfortunate, -// but previous attempts to provide the SSE2 functions with runtime -// detection caused numerous issues. The way architecture extensions are -// exposed in GCC/Clang is, sadly, not really suited for one-file libs. -// New behavior: if compiled with -msse2, we use SSE2 without any -// detection; if not, we don't use it at all. -#define STBI_NO_SIMD -#endif - -#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) -// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET -// -// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the -// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. -// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not -// simultaneously enabling "-mstackrealign". -// -// See https://github.com/nothings/stb/issues/81 for more information. -// -// So default to no SSE2 on 32-bit MinGW. If you've read this far and added -// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. -#define STBI_NO_SIMD -#endif - -#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) -#define STBI_SSE2 -#include - -#ifdef _MSC_VER - -#if _MSC_VER >= 1400 // not VC6 -#include // __cpuid -static int stbi__cpuid3(void) -{ - int info[4]; - __cpuid(info,1); - return info[3]; -} -#else -static int stbi__cpuid3(void) -{ - int res; - __asm { - mov eax,1 - cpuid - mov res,edx - } - return res; -} -#endif - -#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name - -static int stbi__sse2_available(void) -{ - int info3 = stbi__cpuid3(); - return ((info3 >> 26) & 1) != 0; -} -#else // assume GCC-style if not VC++ -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) - -static int stbi__sse2_available(void) -{ - // If we're even attempting to compile this on GCC/Clang, that means - // -msse2 is on, which means the compiler is allowed to use SSE2 - // instructions at will, and so are we. - return 1; -} -#endif -#endif - -// ARM NEON -#if defined(STBI_NO_SIMD) && defined(STBI_NEON) -#undef STBI_NEON -#endif - -#ifdef STBI_NEON -#include -// assume GCC or Clang on ARM targets -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) -#endif - -#ifndef STBI_SIMD_ALIGN -#define STBI_SIMD_ALIGN(type, name) type name -#endif - -/////////////////////////////////////////////// -// -// stbi__context struct and start_xxx functions - -// stbi__context structure is our basic context used by all images, so it -// contains all the IO context, plus some basic image information -typedef struct -{ - stbi__uint32 img_x, img_y; - int img_n, img_out_n; - - stbi_io_callbacks io; - void *io_user_data; - - int read_from_callbacks; - int buflen; - stbi_uc buffer_start[128]; - - stbi_uc *img_buffer, *img_buffer_end; - stbi_uc *img_buffer_original, *img_buffer_original_end; -} stbi__context; - - -static void stbi__refill_buffer(stbi__context *s); - -// initialize a memory-decode context -static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) -{ - s->io.read = NULL; - s->read_from_callbacks = 0; - s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; - s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; -} - -// initialize a callback-based context -static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) -{ - s->io = *c; - s->io_user_data = user; - s->buflen = sizeof(s->buffer_start); - s->read_from_callbacks = 1; - s->img_buffer_original = s->buffer_start; - stbi__refill_buffer(s); - s->img_buffer_original_end = s->img_buffer_end; -} - -#ifndef STBI_NO_STDIO - -static int stbi__stdio_read(void *user, char *data, int size) -{ - return (int) fread(data,1,size,(FILE*) user); -} - -static void stbi__stdio_skip(void *user, int n) -{ - fseek((FILE*) user, n, SEEK_CUR); -} - -static int stbi__stdio_eof(void *user) -{ - return feof((FILE*) user); -} - -static stbi_io_callbacks stbi__stdio_callbacks = -{ - stbi__stdio_read, - stbi__stdio_skip, - stbi__stdio_eof, -}; - -static void stbi__start_file(stbi__context *s, FILE *f) -{ - stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); -} - -//static void stop_file(stbi__context *s) { } - -#endif // !STBI_NO_STDIO - -static void stbi__rewind(stbi__context *s) -{ - // conceptually rewind SHOULD rewind to the beginning of the stream, - // but we just rewind to the beginning of the initial buffer, because - // we only use it after doing 'test', which only ever looks at at most 92 bytes - s->img_buffer = s->img_buffer_original; - s->img_buffer_end = s->img_buffer_original_end; -} - -enum -{ - STBI_ORDER_RGB, - STBI_ORDER_BGR -}; - -typedef struct -{ - int bits_per_channel; - int num_channels; - int channel_order; -} stbi__result_info; - -#ifndef STBI_NO_JPEG -static int stbi__jpeg_test(stbi__context *s); -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNG -static int stbi__png_test(stbi__context *s); -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__png_is16(stbi__context *s); -#endif - -#ifndef STBI_NO_BMP -static int stbi__bmp_test(stbi__context *s); -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_TGA -static int stbi__tga_test(stbi__context *s); -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s); -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__psd_is16(stbi__context *s); -#endif - -#ifndef STBI_NO_HDR -static int stbi__hdr_test(stbi__context *s); -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_test(stbi__context *s); -static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_GIF -static int stbi__gif_test(stbi__context *s); -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNM -static int stbi__pnm_test(stbi__context *s); -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -// this is not threadsafe -static const char *stbi__g_failure_reason; - -STBIDEF const char *stbi_failure_reason(void) -{ - return stbi__g_failure_reason; -} - -static int stbi__err(const char *str) -{ - stbi__g_failure_reason = str; - return 0; -} - -static void *stbi__malloc(size_t size) -{ - return STBI_MALLOC(size); -} - -// stb_image uses ints pervasively, including for offset calculations. -// therefore the largest decoded image size we can support with the -// current code, even on 64-bit targets, is INT_MAX. this is not a -// significant limitation for the intended use case. -// -// we do, however, need to make sure our size calculations don't -// overflow. hence a few helper functions for size calculations that -// multiply integers together, making sure that they're non-negative -// and no overflow occurs. - -// return 1 if the sum is valid, 0 on overflow. -// negative terms are considered invalid. -static int stbi__addsizes_valid(int a, int b) -{ - if (b < 0) return 0; - // now 0 <= b <= INT_MAX, hence also - // 0 <= INT_MAX - b <= INTMAX. - // And "a + b <= INT_MAX" (which might overflow) is the - // same as a <= INT_MAX - b (no overflow) - return a <= INT_MAX - b; -} - -// returns 1 if the product is valid, 0 on overflow. -// negative factors are considered invalid. -static int stbi__mul2sizes_valid(int a, int b) -{ - if (a < 0 || b < 0) return 0; - if (b == 0) return 1; // mul-by-0 is always safe - // portable way to check for no overflows in a*b - return a <= INT_MAX/b; -} - -// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow -static int stbi__mad2sizes_valid(int a, int b, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); -} - -// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow -static int stbi__mad3sizes_valid(int a, int b, int c, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__addsizes_valid(a*b*c, add); -} - -// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); -} -#endif - -// mallocs with size overflow checking -static void *stbi__malloc_mad2(int a, int b, int add) -{ - if (!stbi__mad2sizes_valid(a, b, add)) return NULL; - return stbi__malloc(a*b + add); -} - -static void *stbi__malloc_mad3(int a, int b, int c, int add) -{ - if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; - return stbi__malloc(a*b*c + add); -} - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) -{ - if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; - return stbi__malloc(a*b*c*d + add); -} -#endif - -// stbi__err - error -// stbi__errpf - error returning pointer to float -// stbi__errpuc - error returning pointer to unsigned char - -#ifdef STBI_NO_FAILURE_STRINGS - #define stbi__err(x,y) 0 -#elif defined(STBI_FAILURE_USERMSG) - #define stbi__err(x,y) stbi__err(y) -#else - #define stbi__err(x,y) stbi__err(x) -#endif - -#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) -#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) - -STBIDEF void stbi_image_free(void *retval_from_stbi_load) -{ - STBI_FREE(retval_from_stbi_load); -} - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); -#endif - -#ifndef STBI_NO_HDR -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); -#endif - -static int stbi__vertically_flip_on_load = 0; - -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load = flag_true_if_should_flip; -} - -static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields - ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed - ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order - ri->num_channels = 0; - - #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PNG - if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_BMP - if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_GIF - if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PSD - if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); - #endif - #ifndef STBI_NO_PIC - if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PNM - if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); - return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); - } - #endif - - #ifndef STBI_NO_TGA - // test tga last because it's a crappy test! - if (stbi__tga_test(s)) - return stbi__tga_load(s,x,y,comp,req_comp, ri); - #endif - - return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); -} - -static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi_uc *reduced; - - reduced = (stbi_uc *) stbi__malloc(img_len); - if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling - - STBI_FREE(orig); - return reduced; -} - -static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi__uint16 *enlarged; - - enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); - if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff - - STBI_FREE(orig); - return enlarged; -} - -static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) -{ - int row; - size_t bytes_per_row = (size_t)w * bytes_per_pixel; - stbi_uc temp[2048]; - stbi_uc *bytes = (stbi_uc *)image; - - for (row = 0; row < (h>>1); row++) { - stbi_uc *row0 = bytes + row*bytes_per_row; - stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; - // swap row0 with row1 - size_t bytes_left = bytes_per_row; - while (bytes_left) { - size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); - memcpy(temp, row0, bytes_copy); - memcpy(row0, row1, bytes_copy); - memcpy(row1, temp, bytes_copy); - row0 += bytes_copy; - row1 += bytes_copy; - bytes_left -= bytes_copy; - } - } -} - -static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) -{ - int slice; - int slice_size = w * h * bytes_per_pixel; - - stbi_uc *bytes = (stbi_uc *)image; - for (slice = 0; slice < z; ++slice) { - stbi__vertical_flip(bytes, w, h, bytes_per_pixel); - bytes += slice_size; - } -} - -static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); - - if (result == NULL) - return NULL; - - if (ri.bits_per_channel != 8) { - STBI_ASSERT(ri.bits_per_channel == 16); - result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 8; - } - - // @TODO: move stbi__convert_format to here - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); - } - - return (unsigned char *) result; -} - -static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); - - if (result == NULL) - return NULL; - - if (ri.bits_per_channel != 16) { - STBI_ASSERT(ri.bits_per_channel == 8); - result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 16; - } - - // @TODO: move stbi__convert_format16 to here - // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); - } - - return (stbi__uint16 *) result; -} - -#if !defined(STBI_NO_HDR) || !defined(STBI_NO_LINEAR) -static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) -{ - if (stbi__vertically_flip_on_load && result != NULL) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); - } -} -#endif - -#ifndef STBI_NO_STDIO - -static FILE *stbi__fopen(char const *filename, char const *mode) -{ - FILE *f; -#if defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != fopen_s(&f, filename, mode)) - f=0; -#else - f = fopen(filename, mode); -#endif - return f; -} - - -STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - unsigned char *result; - if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} - -STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__uint16 *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} - -STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - stbi__uint16 *result; - if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file_16(f,x,y,comp,req_comp); - fclose(f); - return result; -} - - -#endif //!STBI_NO_STDIO - -STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - -STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); -} - -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_mem(&s,buffer,len); - - result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); - if (stbi__vertically_flip_on_load) { - stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); - } - - return result; -} -#endif - -#ifndef STBI_NO_LINEAR -static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *data; - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - stbi__result_info ri; - float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); - if (hdr_data) - stbi__float_postprocess(hdr_data,x,y,comp,req_comp); - return hdr_data; - } - #endif - data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); - if (data) - return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); - return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); -} - -STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_STDIO -STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - float *result; - FILE *f = stbi__fopen(filename, "rb"); - if (!f) return stbi__errpf("can't fopen", "Unable to open file"); - result = stbi_loadf_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_file(&s,f); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} -#endif // !STBI_NO_STDIO - -#endif // !STBI_NO_LINEAR - -// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is -// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always -// reports false! - -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(buffer); - STBI_NOTUSED(len); - return 0; - #endif -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result=0; - if (f) { - result = stbi_is_hdr_from_file(f); - fclose(f); - } - return result; -} - -STBIDEF int stbi_is_hdr_from_file(FILE *f) -{ - #ifndef STBI_NO_HDR - long pos = ftell(f); - int res; - stbi__context s; - stbi__start_file(&s,f); - res = stbi__hdr_test(&s); - fseek(f, pos, SEEK_SET); - return res; - #else - STBI_NOTUSED(f); - return 0; - #endif -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(clbk); - STBI_NOTUSED(user); - return 0; - #endif -} - -#ifndef STBI_NO_LINEAR -static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; - -STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } -STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } -#endif - -static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; - -STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } -STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } - - -////////////////////////////////////////////////////////////////////////////// -// -// Common code used by all image loaders -// - -enum -{ - STBI__SCAN_load=0, - STBI__SCAN_type, - STBI__SCAN_header -}; - -static void stbi__refill_buffer(stbi__context *s) -{ - int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); - if (n == 0) { - // at end of file, treat same as if from memory, but need to handle case - // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file - s->read_from_callbacks = 0; - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start+1; - *s->img_buffer = 0; - } else { - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start + n; - } -} - -stbi_inline static stbi_uc stbi__get8(stbi__context *s) -{ - if (s->img_buffer < s->img_buffer_end) - return *s->img_buffer++; - if (s->read_from_callbacks) { - stbi__refill_buffer(s); - return *s->img_buffer++; - } - return 0; -} - -stbi_inline static int stbi__at_eof(stbi__context *s) -{ - if (s->io.read) { - if (!(s->io.eof)(s->io_user_data)) return 0; - // if feof() is true, check if buffer = end - // special case: we've only got the special 0 character at the end - if (s->read_from_callbacks == 0) return 1; - } - - return s->img_buffer >= s->img_buffer_end; -} - -static void stbi__skip(stbi__context *s, int n) -{ - if (n < 0) { - s->img_buffer = s->img_buffer_end; - return; - } - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - s->img_buffer = s->img_buffer_end; - (s->io.skip)(s->io_user_data, n - blen); - return; - } - } - s->img_buffer += n; -} - -static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) -{ - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - int res, count; - - memcpy(buffer, s->img_buffer, blen); - - count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); - res = (count == (n-blen)); - s->img_buffer = s->img_buffer_end; - return res; - } - } - - if (s->img_buffer+n <= s->img_buffer_end) { - memcpy(buffer, s->img_buffer, n); - s->img_buffer += n; - return 1; - } else - return 0; -} - -static int stbi__get16be(stbi__context *s) -{ - int z = stbi__get8(s); - return (z << 8) + stbi__get8(s); -} - -static stbi__uint32 stbi__get32be(stbi__context *s) -{ - stbi__uint32 z = stbi__get16be(s); - return (z << 16) + stbi__get16be(s); -} - -#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) -// nothing -#else -static int stbi__get16le(stbi__context *s) -{ - int z = stbi__get8(s); - return z + (stbi__get8(s) << 8); -} -#endif - -#ifndef STBI_NO_BMP -static stbi__uint32 stbi__get32le(stbi__context *s) -{ - stbi__uint32 z = stbi__get16le(s); - return z + (stbi__get16le(s) << 16); -} -#endif - -#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings - - -////////////////////////////////////////////////////////////////////////////// -// -// generic converter from built-in img_n to req_comp -// individual types do this automatically as much as possible (e.g. jpeg -// does all cases internally since it needs to colorspace convert anyway, -// and it never has alpha, so very few cases ). png can automatically -// interleave an alpha=255 channel, but falls back to this for other cases -// -// assume data buffer is malloced, so malloc a new one and free that one -// only failure mode is malloc failing - -static stbi_uc stbi__compute_y(int r, int g, int b) -{ - return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); -} - -static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - unsigned char *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); - if (good == NULL) { - STBI_FREE(data); - return stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - unsigned char *src = data + j * x * img_n ; - unsigned char *dest = good + j * x * req_comp; - - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0], dest[1]=255; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; - default: STBI_ASSERT(0); - } - #undef STBI__CASE - } - - STBI_FREE(data); - return good; -} - -static stbi__uint16 stbi__compute_y_16(int r, int g, int b) -{ - return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); -} - -static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - stbi__uint16 *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); - if (good == NULL) { - STBI_FREE(data); - return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - stbi__uint16 *src = data + j * x * img_n ; - stbi__uint16 *dest = good + j * x * req_comp; - - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0], dest[1]=0xffff; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=0xffff; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=0xffff; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = 0xffff; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; - default: STBI_ASSERT(0); - } - #undef STBI__CASE - } - - STBI_FREE(data); - return good; -} - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) -{ - int i,k,n; - float *output; - if (!data) return NULL; - output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); - } - if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; - } - STBI_FREE(data); - return output; -} -#endif - -#ifndef STBI_NO_HDR -#define stbi__float2int(x) ((int) (x)) -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) -{ - int i,k,n; - stbi_uc *output; - if (!data) return NULL; - output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - if (k < comp) { - float z = data[i*comp+k] * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - } - STBI_FREE(data); - return output; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// "baseline" JPEG/JFIF decoder -// -// simple implementation -// - doesn't support delayed output of y-dimension -// - simple interface (only one output format: 8-bit interleaved RGB) -// - doesn't try to recover corrupt jpegs -// - doesn't allow partial loading, loading multiple at once -// - still fast on x86 (copying globals into locals doesn't help x86) -// - allocates lots of intermediate memory (full size of all components) -// - non-interleaved case requires this anyway -// - allows good upsampling (see next) -// high-quality -// - upsampled channels are bilinearly interpolated, even across blocks -// - quality integer IDCT derived from IJG's 'slow' -// performance -// - fast huffman; reasonable integer IDCT -// - some SIMD kernels for common paths on targets with SSE2/NEON -// - uses a lot of intermediate memory, could cache poorly - -#ifndef STBI_NO_JPEG - -// huffman decoding acceleration -#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache - -typedef struct -{ - stbi_uc fast[1 << FAST_BITS]; - // weirdly, repacking this into AoS is a 10% speed loss, instead of a win - stbi__uint16 code[256]; - stbi_uc values[256]; - stbi_uc size[257]; - unsigned int maxcode[18]; - int delta[17]; // old 'firstsymbol' - old 'firstcode' -} stbi__huffman; - -typedef struct -{ - stbi__context *s; - stbi__huffman huff_dc[4]; - stbi__huffman huff_ac[4]; - stbi__uint16 dequant[4][64]; - stbi__int16 fast_ac[4][1 << FAST_BITS]; - -// sizes for components, interleaved MCUs - int img_h_max, img_v_max; - int img_mcu_x, img_mcu_y; - int img_mcu_w, img_mcu_h; - -// definition of jpeg image component - struct - { - int id; - int h,v; - int tq; - int hd,ha; - int dc_pred; - - int x,y,w2,h2; - stbi_uc *data; - void *raw_data, *raw_coeff; - stbi_uc *linebuf; - short *coeff; // progressive only - int coeff_w, coeff_h; // number of 8x8 coefficient blocks - } img_comp[4]; - - stbi__uint32 code_buffer; // jpeg entropy-coded buffer - int code_bits; // number of valid bits - unsigned char marker; // marker seen while filling entropy buffer - int nomore; // flag if we saw a marker so must stop - - int progressive; - int spec_start; - int spec_end; - int succ_high; - int succ_low; - int eob_run; - int jfif; - int app14_color_transform; // Adobe APP14 tag - int rgb; - - int scan_n, order[4]; - int restart_interval, todo; - -// kernels - void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); - void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); - stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); -} stbi__jpeg; - -static int stbi__build_huffman(stbi__huffman *h, int *count) -{ - int i,j,k=0; - unsigned int code; - // build size list for each symbol (from JPEG spec) - for (i=0; i < 16; ++i) - for (j=0; j < count[i]; ++j) - h->size[k++] = (stbi_uc) (i+1); - h->size[k] = 0; - - // compute actual symbols (from jpeg spec) - code = 0; - k = 0; - for(j=1; j <= 16; ++j) { - // compute delta to add to code to compute symbol id - h->delta[j] = k - code; - if (h->size[k] == j) { - while (h->size[k] == j) - h->code[k++] = (stbi__uint16) (code++); - if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); - } - // compute largest code + 1 for this size, preshifted as needed later - h->maxcode[j] = code << (16-j); - code <<= 1; - } - h->maxcode[j] = 0xffffffff; - - // build non-spec acceleration table; 255 is flag for not-accelerated - memset(h->fast, 255, 1 << FAST_BITS); - for (i=0; i < k; ++i) { - int s = h->size[i]; - if (s <= FAST_BITS) { - int c = h->code[i] << (FAST_BITS-s); - int m = 1 << (FAST_BITS-s); - for (j=0; j < m; ++j) { - h->fast[c+j] = (stbi_uc) i; - } - } - } - return 1; -} - -// build a table that decodes both magnitude and value of small ACs in -// one go. -static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) -{ - int i; - for (i=0; i < (1 << FAST_BITS); ++i) { - stbi_uc fast = h->fast[i]; - fast_ac[i] = 0; - if (fast < 255) { - int rs = h->values[fast]; - int run = (rs >> 4) & 15; - int magbits = rs & 15; - int len = h->size[fast]; - - if (magbits && len + magbits <= FAST_BITS) { - // magnitude code followed by receive_extend code - int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); - int m = 1 << (magbits - 1); - if (k < m) k += (~0U << magbits) + 1; - // if the result is small enough, we can fit it in fast_ac table - if (k >= -128 && k <= 127) - fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); - } - } - } -} - -static void stbi__grow_buffer_unsafe(stbi__jpeg *j) -{ - do { - unsigned int b = j->nomore ? 0 : stbi__get8(j->s); - if (b == 0xff) { - int c = stbi__get8(j->s); - while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes - if (c != 0) { - j->marker = (unsigned char) c; - j->nomore = 1; - return; - } - } - j->code_buffer |= b << (24 - j->code_bits); - j->code_bits += 8; - } while (j->code_bits <= 24); -} - -// (1 << n) - 1 -static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; - -// decode a jpeg huffman value from the bitstream -stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) -{ - unsigned int temp; - int c,k; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - // look at the top FAST_BITS and determine what symbol ID it is, - // if the code is <= FAST_BITS - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - k = h->fast[c]; - if (k < 255) { - int s = h->size[k]; - if (s > j->code_bits) - return -1; - j->code_buffer <<= s; - j->code_bits -= s; - return h->values[k]; - } - - // naive test is to shift the code_buffer down so k bits are - // valid, then test against maxcode. To speed this up, we've - // preshifted maxcode left so that it has (16-k) 0s at the - // end; in other words, regardless of the number of bits, it - // wants to be compared against something shifted to have 16; - // that way we don't need to shift inside the loop. - temp = j->code_buffer >> 16; - for (k=FAST_BITS+1 ; ; ++k) - if (temp < h->maxcode[k]) - break; - if (k == 17) { - // error! code not found - j->code_bits -= 16; - return -1; - } - - if (k > j->code_bits) - return -1; - - // convert the huffman code to the symbol id - c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; - STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); - - // convert the id to a symbol - j->code_bits -= k; - j->code_buffer <<= k; - return h->values[c]; -} - -// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); - - sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB - k = stbi_lrot(j->code_buffer, n); - STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k + (stbi__jbias[n] & ~sgn); -} - -// get some unsigned bits -stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) -{ - unsigned int k; - if (j->code_bits < n) stbi__grow_buffer_unsafe(j); - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k; -} - -stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) -{ - unsigned int k; - if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); - k = j->code_buffer; - j->code_buffer <<= 1; - --j->code_bits; - return k & 0x80000000; -} - -// given a value that's at position X in the zigzag stream, -// where does it appear in the 8x8 matrix coded as row-major? -static const stbi_uc stbi__jpeg_dezigzag[64+15] = -{ - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63, - // let corrupt input sample past end - 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63 -}; - -// decode one 64-entry block-- -static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) -{ - int diff,dc,k; - int t; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - - // 0 all the ac values now so we can do it 32-bits at a time - memset(data,0,64*sizeof(data[0])); - - diff = t ? stbi__extend_receive(j, t) : 0; - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc * dequant[0]); - - // decode AC components, see JPEG spec - k = 1; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) * dequant[zig]); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (rs != 0xf0) break; // end block - k += 16; - } else { - k += r; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); - } - } - } while (k < 64); - return 1; -} - -static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) -{ - int diff,dc; - int t; - if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - if (j->succ_high == 0) { - // first scan for DC coefficient, must be first - memset(data,0,64*sizeof(data[0])); // 0 all the ac values now - t = stbi__jpeg_huff_decode(j, hdc); - diff = t ? stbi__extend_receive(j, t) : 0; - - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc << j->succ_low); - } else { - // refinement scan for DC coefficient - if (stbi__jpeg_get_bit(j)) - data[0] += (short) (1 << j->succ_low); - } - return 1; -} - -// @OPTIMIZE: store non-zigzagged during the decode passes, -// and only de-zigzag when dequantizing -static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) -{ - int k; - if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->succ_high == 0) { - int shift = j->succ_low; - - if (j->eob_run) { - --j->eob_run; - return 1; - } - - k = j->spec_start; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) << shift); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r); - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - --j->eob_run; - break; - } - k += 16; - } else { - k += r; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) << shift); - } - } - } while (k <= j->spec_end); - } else { - // refinement scan for these AC coefficients - - short bit = (short) (1 << j->succ_low); - - if (j->eob_run) { - --j->eob_run; - for (k = j->spec_start; k <= j->spec_end; ++k) { - short *p = &data[stbi__jpeg_dezigzag[k]]; - if (*p != 0) - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } - } else { - k = j->spec_start; - do { - int r,s; - int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r) - 1; - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - r = 64; // force end of block - } else { - // r=15 s=0 should write 16 0s, so we just do - // a run of 15 0s and then write s (which is 0), - // so we don't have to do anything special here - } - } else { - if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); - // sign bit - if (stbi__jpeg_get_bit(j)) - s = bit; - else - s = -bit; - } - - // advance by r - while (k <= j->spec_end) { - short *p = &data[stbi__jpeg_dezigzag[k++]]; - if (*p != 0) { - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } else { - if (r == 0) { - *p = (short) s; - break; - } - --r; - } - } - } while (k <= j->spec_end); - } - } - return 1; -} - -// take a -128..127 value and stbi__clamp it and convert to 0..255 -stbi_inline static stbi_uc stbi__clamp(int x) -{ - // trick to use a single test to catch both cases - if ((unsigned int) x > 255) { - if (x < 0) return 0; - if (x > 255) return 255; - } - return (stbi_uc) x; -} - -#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) -#define stbi__fsh(x) ((x) * 4096) - -// derived from jidctint -- DCT_ISLOW -#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ - int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ - p2 = s2; \ - p3 = s6; \ - p1 = (p2+p3) * stbi__f2f(0.5411961f); \ - t2 = p1 + p3*stbi__f2f(-1.847759065f); \ - t3 = p1 + p2*stbi__f2f( 0.765366865f); \ - p2 = s0; \ - p3 = s4; \ - t0 = stbi__fsh(p2+p3); \ - t1 = stbi__fsh(p2-p3); \ - x0 = t0+t3; \ - x3 = t0-t3; \ - x1 = t1+t2; \ - x2 = t1-t2; \ - t0 = s7; \ - t1 = s5; \ - t2 = s3; \ - t3 = s1; \ - p3 = t0+t2; \ - p4 = t1+t3; \ - p1 = t0+t3; \ - p2 = t1+t2; \ - p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ - t0 = t0*stbi__f2f( 0.298631336f); \ - t1 = t1*stbi__f2f( 2.053119869f); \ - t2 = t2*stbi__f2f( 3.072711026f); \ - t3 = t3*stbi__f2f( 1.501321110f); \ - p1 = p5 + p1*stbi__f2f(-0.899976223f); \ - p2 = p5 + p2*stbi__f2f(-2.562915447f); \ - p3 = p3*stbi__f2f(-1.961570560f); \ - p4 = p4*stbi__f2f(-0.390180644f); \ - t3 += p1+p4; \ - t2 += p2+p3; \ - t1 += p2+p4; \ - t0 += p1+p3; - -static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) -{ - int i,val[64],*v=val; - stbi_uc *o; - short *d = data; - - // columns - for (i=0; i < 8; ++i,++d, ++v) { - // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing - if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 - && d[40]==0 && d[48]==0 && d[56]==0) { - // no shortcut 0 seconds - // (1|2|3|4|5|6|7)==0 0 seconds - // all separate -0.047 seconds - // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds - int dcterm = d[0]*4; - v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; - } else { - STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) - // constants scaled things up by 1<<12; let's bring them back - // down, but keep 2 extra bits of precision - x0 += 512; x1 += 512; x2 += 512; x3 += 512; - v[ 0] = (x0+t3) >> 10; - v[56] = (x0-t3) >> 10; - v[ 8] = (x1+t2) >> 10; - v[48] = (x1-t2) >> 10; - v[16] = (x2+t1) >> 10; - v[40] = (x2-t1) >> 10; - v[24] = (x3+t0) >> 10; - v[32] = (x3-t0) >> 10; - } - } - - for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { - // no fast case since the first 1D IDCT spread components out - STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) - // constants scaled things up by 1<<12, plus we had 1<<2 from first - // loop, plus horizontal and vertical each scale by sqrt(8) so together - // we've got an extra 1<<3, so 1<<17 total we need to remove. - // so we want to round that, which means adding 0.5 * 1<<17, - // aka 65536. Also, we'll end up with -128 to 127 that we want - // to encode as 0..255 by adding 128, so we'll add that before the shift - x0 += 65536 + (128<<17); - x1 += 65536 + (128<<17); - x2 += 65536 + (128<<17); - x3 += 65536 + (128<<17); - // tried computing the shifts into temps, or'ing the temps to see - // if any were out of range, but that was slower - o[0] = stbi__clamp((x0+t3) >> 17); - o[7] = stbi__clamp((x0-t3) >> 17); - o[1] = stbi__clamp((x1+t2) >> 17); - o[6] = stbi__clamp((x1-t2) >> 17); - o[2] = stbi__clamp((x2+t1) >> 17); - o[5] = stbi__clamp((x2-t1) >> 17); - o[3] = stbi__clamp((x3+t0) >> 17); - o[4] = stbi__clamp((x3-t0) >> 17); - } -} - -#ifdef STBI_SSE2 -// sse2 integer IDCT. not the fastest possible implementation but it -// produces bit-identical results to the generic C version so it's -// fully "transparent". -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - // This is constructed to match our regular (generic) integer IDCT exactly. - __m128i row0, row1, row2, row3, row4, row5, row6, row7; - __m128i tmp; - - // dot product constant: even elems=x, odd elems=y - #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) - - // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) - // out(1) = c1[even]*x + c1[odd]*y - #define dct_rot(out0,out1, x,y,c0,c1) \ - __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ - __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ - __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ - __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ - __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ - __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) - - // out = in << 12 (in 16-bit, out 32-bit) - #define dct_widen(out, in) \ - __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ - __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) - - // wide add - #define dct_wadd(out, a, b) \ - __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_add_epi32(a##_h, b##_h) - - // wide sub - #define dct_wsub(out, a, b) \ - __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) - - // butterfly a/b, add bias, then shift by "s" and pack - #define dct_bfly32o(out0, out1, a,b,bias,s) \ - { \ - __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ - __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ - dct_wadd(sum, abiased, b); \ - dct_wsub(dif, abiased, b); \ - out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ - out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ - } - - // 8-bit interleave step (for transposes) - #define dct_interleave8(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi8(a, b); \ - b = _mm_unpackhi_epi8(tmp, b) - - // 16-bit interleave step (for transposes) - #define dct_interleave16(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi16(a, b); \ - b = _mm_unpackhi_epi16(tmp, b) - - #define dct_pass(bias,shift) \ - { \ - /* even part */ \ - dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ - __m128i sum04 = _mm_add_epi16(row0, row4); \ - __m128i dif04 = _mm_sub_epi16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ - dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ - __m128i sum17 = _mm_add_epi16(row1, row7); \ - __m128i sum35 = _mm_add_epi16(row3, row5); \ - dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ - dct_wadd(x4, y0o, y4o); \ - dct_wadd(x5, y1o, y5o); \ - dct_wadd(x6, y2o, y5o); \ - dct_wadd(x7, y3o, y4o); \ - dct_bfly32o(row0,row7, x0,x7,bias,shift); \ - dct_bfly32o(row1,row6, x1,x6,bias,shift); \ - dct_bfly32o(row2,row5, x2,x5,bias,shift); \ - dct_bfly32o(row3,row4, x3,x4,bias,shift); \ - } - - __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); - __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); - __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); - __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); - __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); - __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); - __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); - __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); - - // rounding biases in column/row passes, see stbi__idct_block for explanation. - __m128i bias_0 = _mm_set1_epi32(512); - __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); - - // load - row0 = _mm_load_si128((const __m128i *) (data + 0*8)); - row1 = _mm_load_si128((const __m128i *) (data + 1*8)); - row2 = _mm_load_si128((const __m128i *) (data + 2*8)); - row3 = _mm_load_si128((const __m128i *) (data + 3*8)); - row4 = _mm_load_si128((const __m128i *) (data + 4*8)); - row5 = _mm_load_si128((const __m128i *) (data + 5*8)); - row6 = _mm_load_si128((const __m128i *) (data + 6*8)); - row7 = _mm_load_si128((const __m128i *) (data + 7*8)); - - // column pass - dct_pass(bias_0, 10); - - { - // 16bit 8x8 transpose pass 1 - dct_interleave16(row0, row4); - dct_interleave16(row1, row5); - dct_interleave16(row2, row6); - dct_interleave16(row3, row7); - - // transpose pass 2 - dct_interleave16(row0, row2); - dct_interleave16(row1, row3); - dct_interleave16(row4, row6); - dct_interleave16(row5, row7); - - // transpose pass 3 - dct_interleave16(row0, row1); - dct_interleave16(row2, row3); - dct_interleave16(row4, row5); - dct_interleave16(row6, row7); - } - - // row pass - dct_pass(bias_1, 17); - - { - // pack - __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 - __m128i p1 = _mm_packus_epi16(row2, row3); - __m128i p2 = _mm_packus_epi16(row4, row5); - __m128i p3 = _mm_packus_epi16(row6, row7); - - // 8bit 8x8 transpose pass 1 - dct_interleave8(p0, p2); // a0e0a1e1... - dct_interleave8(p1, p3); // c0g0c1g1... - - // transpose pass 2 - dct_interleave8(p0, p1); // a0c0e0g0... - dct_interleave8(p2, p3); // b0d0f0h0... - - // transpose pass 3 - dct_interleave8(p0, p2); // a0b0c0d0... - dct_interleave8(p1, p3); // a4b4c4d4... - - // store - _mm_storel_epi64((__m128i *) out, p0); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p2); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p1); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p3); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); - } - -#undef dct_const -#undef dct_rot -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_interleave8 -#undef dct_interleave16 -#undef dct_pass -} - -#endif // STBI_SSE2 - -#ifdef STBI_NEON - -// NEON integer IDCT. should produce bit-identical -// results to the generic C version. -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; - - int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); - int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); - int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); - int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); - int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); - int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); - int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); - int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); - int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); - int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); - int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); - int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); - -#define dct_long_mul(out, inq, coeff) \ - int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) - -#define dct_long_mac(out, acc, inq, coeff) \ - int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) - -#define dct_widen(out, inq) \ - int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ - int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) - -// wide add -#define dct_wadd(out, a, b) \ - int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vaddq_s32(a##_h, b##_h) - -// wide sub -#define dct_wsub(out, a, b) \ - int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vsubq_s32(a##_h, b##_h) - -// butterfly a/b, then shift using "shiftop" by "s" and pack -#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ - { \ - dct_wadd(sum, a, b); \ - dct_wsub(dif, a, b); \ - out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ - out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ - } - -#define dct_pass(shiftop, shift) \ - { \ - /* even part */ \ - int16x8_t sum26 = vaddq_s16(row2, row6); \ - dct_long_mul(p1e, sum26, rot0_0); \ - dct_long_mac(t2e, p1e, row6, rot0_1); \ - dct_long_mac(t3e, p1e, row2, rot0_2); \ - int16x8_t sum04 = vaddq_s16(row0, row4); \ - int16x8_t dif04 = vsubq_s16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - int16x8_t sum15 = vaddq_s16(row1, row5); \ - int16x8_t sum17 = vaddq_s16(row1, row7); \ - int16x8_t sum35 = vaddq_s16(row3, row5); \ - int16x8_t sum37 = vaddq_s16(row3, row7); \ - int16x8_t sumodd = vaddq_s16(sum17, sum35); \ - dct_long_mul(p5o, sumodd, rot1_0); \ - dct_long_mac(p1o, p5o, sum17, rot1_1); \ - dct_long_mac(p2o, p5o, sum35, rot1_2); \ - dct_long_mul(p3o, sum37, rot2_0); \ - dct_long_mul(p4o, sum15, rot2_1); \ - dct_wadd(sump13o, p1o, p3o); \ - dct_wadd(sump24o, p2o, p4o); \ - dct_wadd(sump23o, p2o, p3o); \ - dct_wadd(sump14o, p1o, p4o); \ - dct_long_mac(x4, sump13o, row7, rot3_0); \ - dct_long_mac(x5, sump24o, row5, rot3_1); \ - dct_long_mac(x6, sump23o, row3, rot3_2); \ - dct_long_mac(x7, sump14o, row1, rot3_3); \ - dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ - dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ - dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ - dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ - } - - // load - row0 = vld1q_s16(data + 0*8); - row1 = vld1q_s16(data + 1*8); - row2 = vld1q_s16(data + 2*8); - row3 = vld1q_s16(data + 3*8); - row4 = vld1q_s16(data + 4*8); - row5 = vld1q_s16(data + 5*8); - row6 = vld1q_s16(data + 6*8); - row7 = vld1q_s16(data + 7*8); - - // add DC bias - row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); - - // column pass - dct_pass(vrshrn_n_s32, 10); - - // 16bit 8x8 transpose - { -// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. -// whether compilers actually get this is another story, sadly. -#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } -#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } - - // pass 1 - dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 - dct_trn16(row2, row3); - dct_trn16(row4, row5); - dct_trn16(row6, row7); - - // pass 2 - dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 - dct_trn32(row1, row3); - dct_trn32(row4, row6); - dct_trn32(row5, row7); - - // pass 3 - dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 - dct_trn64(row1, row5); - dct_trn64(row2, row6); - dct_trn64(row3, row7); - -#undef dct_trn16 -#undef dct_trn32 -#undef dct_trn64 - } - - // row pass - // vrshrn_n_s32 only supports shifts up to 16, we need - // 17. so do a non-rounding shift of 16 first then follow - // up with a rounding shift by 1. - dct_pass(vshrn_n_s32, 16); - - { - // pack and round - uint8x8_t p0 = vqrshrun_n_s16(row0, 1); - uint8x8_t p1 = vqrshrun_n_s16(row1, 1); - uint8x8_t p2 = vqrshrun_n_s16(row2, 1); - uint8x8_t p3 = vqrshrun_n_s16(row3, 1); - uint8x8_t p4 = vqrshrun_n_s16(row4, 1); - uint8x8_t p5 = vqrshrun_n_s16(row5, 1); - uint8x8_t p6 = vqrshrun_n_s16(row6, 1); - uint8x8_t p7 = vqrshrun_n_s16(row7, 1); - - // again, these can translate into one instruction, but often don't. -#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } -#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } - - // sadly can't use interleaved stores here since we only write - // 8 bytes to each scan line! - - // 8x8 8-bit transpose pass 1 - dct_trn8_8(p0, p1); - dct_trn8_8(p2, p3); - dct_trn8_8(p4, p5); - dct_trn8_8(p6, p7); - - // pass 2 - dct_trn8_16(p0, p2); - dct_trn8_16(p1, p3); - dct_trn8_16(p4, p6); - dct_trn8_16(p5, p7); - - // pass 3 - dct_trn8_32(p0, p4); - dct_trn8_32(p1, p5); - dct_trn8_32(p2, p6); - dct_trn8_32(p3, p7); - - // store - vst1_u8(out, p0); out += out_stride; - vst1_u8(out, p1); out += out_stride; - vst1_u8(out, p2); out += out_stride; - vst1_u8(out, p3); out += out_stride; - vst1_u8(out, p4); out += out_stride; - vst1_u8(out, p5); out += out_stride; - vst1_u8(out, p6); out += out_stride; - vst1_u8(out, p7); - -#undef dct_trn8_8 -#undef dct_trn8_16 -#undef dct_trn8_32 - } - -#undef dct_long_mul -#undef dct_long_mac -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_pass -} - -#endif // STBI_NEON - -#define STBI__MARKER_none 0xff -// if there's a pending marker from the entropy stream, return that -// otherwise, fetch from the stream and get a marker. if there's no -// marker, return 0xff, which is never a valid marker value -static stbi_uc stbi__get_marker(stbi__jpeg *j) -{ - stbi_uc x; - if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } - x = stbi__get8(j->s); - if (x != 0xff) return STBI__MARKER_none; - while (x == 0xff) - x = stbi__get8(j->s); // consume repeated 0xff fill bytes - return x; -} - -// in each scan, we'll have scan_n components, and the order -// of the components is specified by order[] -#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) - -// after a restart interval, stbi__jpeg_reset the entropy decoder and -// the dc prediction -static void stbi__jpeg_reset(stbi__jpeg *j) -{ - j->code_bits = 0; - j->code_buffer = 0; - j->nomore = 0; - j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; - j->marker = STBI__MARKER_none; - j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; - j->eob_run = 0; - // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, - // since we don't even allow 1<<30 pixels -} - -static int stbi__parse_entropy_coded_data(stbi__jpeg *z) -{ - stbi__jpeg_reset(z); - if (!z->progressive) { - if (z->scan_n == 1) { - int i,j; - STBI_SIMD_ALIGN(short, data[64]); - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - // if it's NOT a restart, then just bail, so we get corrupt data - // rather than no data - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - STBI_SIMD_ALIGN(short, data[64]); - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x)*8; - int y2 = (j*z->img_comp[n].v + y)*8; - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } else { - if (z->scan_n == 1) { - int i,j; - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - if (z->spec_start == 0) { - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } else { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) - return 0; - } - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x); - int y2 = (j*z->img_comp[n].v + y); - short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } -} - -static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) -{ - int i; - for (i=0; i < 64; ++i) - data[i] *= dequant[i]; -} - -static void stbi__jpeg_finish(stbi__jpeg *z) -{ - if (z->progressive) { - // dequantize and idct the data - int i,j,n; - for (n=0; n < z->s->img_n; ++n) { - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - } - } - } - } -} - -static int stbi__process_marker(stbi__jpeg *z, int m) -{ - int L; - switch (m) { - case STBI__MARKER_none: // no marker found - return stbi__err("expected marker","Corrupt JPEG"); - - case 0xDD: // DRI - specify restart interval - if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); - z->restart_interval = stbi__get16be(z->s); - return 1; - - case 0xDB: // DQT - define quantization table - L = stbi__get16be(z->s)-2; - while (L > 0) { - int q = stbi__get8(z->s); - int p = q >> 4, sixteen = (p != 0); - int t = q & 15,i; - if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); - if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); - - for (i=0; i < 64; ++i) - z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); - L -= (sixteen ? 129 : 65); - } - return L==0; - - case 0xC4: // DHT - define huffman table - L = stbi__get16be(z->s)-2; - while (L > 0) { - stbi_uc *v; - int sizes[16],i,n=0; - int q = stbi__get8(z->s); - int tc = q >> 4; - int th = q & 15; - if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); - for (i=0; i < 16; ++i) { - sizes[i] = stbi__get8(z->s); - n += sizes[i]; - } - L -= 17; - if (tc == 0) { - if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; - v = z->huff_dc[th].values; - } else { - if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; - v = z->huff_ac[th].values; - } - for (i=0; i < n; ++i) - v[i] = stbi__get8(z->s); - if (tc != 0) - stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); - L -= n; - } - return L==0; - } - - // check for comment block or APP blocks - if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - L = stbi__get16be(z->s); - if (L < 2) { - if (m == 0xFE) - return stbi__err("bad COM len","Corrupt JPEG"); - else - return stbi__err("bad APP len","Corrupt JPEG"); - } - L -= 2; - - if (m == 0xE0 && L >= 5) { // JFIF APP0 segment - static const unsigned char tag[5] = {'J','F','I','F','\0'}; - int ok = 1; - int i; - for (i=0; i < 5; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 5; - if (ok) - z->jfif = 1; - } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment - static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; - int ok = 1; - int i; - for (i=0; i < 6; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 6; - if (ok) { - stbi__get8(z->s); // version - stbi__get16be(z->s); // flags0 - stbi__get16be(z->s); // flags1 - z->app14_color_transform = stbi__get8(z->s); // color transform - L -= 6; - } - } - - stbi__skip(z->s, L); - return 1; - } - - return stbi__err("unknown marker","Corrupt JPEG"); -} - -// after we see SOS -static int stbi__process_scan_header(stbi__jpeg *z) -{ - int i; - int Ls = stbi__get16be(z->s); - z->scan_n = stbi__get8(z->s); - if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); - if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); - for (i=0; i < z->scan_n; ++i) { - int id = stbi__get8(z->s), which; - int q = stbi__get8(z->s); - for (which = 0; which < z->s->img_n; ++which) - if (z->img_comp[which].id == id) - break; - if (which == z->s->img_n) return 0; // no match - z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); - z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); - z->order[i] = which; - } - - { - int aa; - z->spec_start = stbi__get8(z->s); - z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 - aa = stbi__get8(z->s); - z->succ_high = (aa >> 4); - z->succ_low = (aa & 15); - if (z->progressive) { - if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) - return stbi__err("bad SOS", "Corrupt JPEG"); - } else { - if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); - if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); - z->spec_end = 63; - } - } - - return 1; -} - -static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) -{ - int i; - for (i=0; i < ncomp; ++i) { - if (z->img_comp[i].raw_data) { - STBI_FREE(z->img_comp[i].raw_data); - z->img_comp[i].raw_data = NULL; - z->img_comp[i].data = NULL; - } - if (z->img_comp[i].raw_coeff) { - STBI_FREE(z->img_comp[i].raw_coeff); - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].coeff = 0; - } - if (z->img_comp[i].linebuf) { - STBI_FREE(z->img_comp[i].linebuf); - z->img_comp[i].linebuf = NULL; - } - } - return why; -} - -static int stbi__process_frame_header(stbi__jpeg *z, int scan) -{ - stbi__context *s = z->s; - int Lf,p,i,q, h_max=1,v_max=1,c; - Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG - p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline - s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG - s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires - c = stbi__get8(s); - if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); - s->img_n = c; - for (i=0; i < c; ++i) { - z->img_comp[i].data = NULL; - z->img_comp[i].linebuf = NULL; - } - - if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); - - z->rgb = 0; - for (i=0; i < s->img_n; ++i) { - static const unsigned char rgb[3] = { 'R', 'G', 'B' }; - z->img_comp[i].id = stbi__get8(s); - if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) - ++z->rgb; - q = stbi__get8(s); - z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); - z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); - z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); - } - - if (scan != STBI__SCAN_load) return 1; - - if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); - - for (i=0; i < s->img_n; ++i) { - if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; - if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; - } - - // compute interleaved mcu info - z->img_h_max = h_max; - z->img_v_max = v_max; - z->img_mcu_w = h_max * 8; - z->img_mcu_h = v_max * 8; - // these sizes can't be more than 17 bits - z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; - z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; - - for (i=0; i < s->img_n; ++i) { - // number of effective pixels (e.g. for non-interleaved MCU) - z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; - z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; - // to simplify generation, we'll allocate enough memory to decode - // the bogus oversized data from using interleaved MCUs and their - // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't - // discard the extra data until colorspace conversion - // - // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) - // so these muls can't overflow with 32-bit ints (which we require) - z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; - z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].coeff = 0; - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].linebuf = NULL; - z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); - if (z->img_comp[i].raw_data == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - // align blocks for idct using mmx/sse - z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); - if (z->progressive) { - // w2, h2 are multiples of 8 (see above) - z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; - z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; - z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); - if (z->img_comp[i].raw_coeff == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); - } - } - - return 1; -} - -// use comparisons since in some cases we handle more than one case (e.g. SOF) -#define stbi__DNL(x) ((x) == 0xdc) -#define stbi__SOI(x) ((x) == 0xd8) -#define stbi__EOI(x) ((x) == 0xd9) -#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) -#define stbi__SOS(x) ((x) == 0xda) - -#define stbi__SOF_progressive(x) ((x) == 0xc2) - -static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) -{ - int m; - z->jfif = 0; - z->app14_color_transform = -1; // valid values are 0,1,2 - z->marker = STBI__MARKER_none; // initialize cached marker to empty - m = stbi__get_marker(z); - if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); - if (scan == STBI__SCAN_type) return 1; - m = stbi__get_marker(z); - while (!stbi__SOF(m)) { - if (!stbi__process_marker(z,m)) return 0; - m = stbi__get_marker(z); - while (m == STBI__MARKER_none) { - // some files have extra padding after their blocks, so ok, we'll scan - if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); - m = stbi__get_marker(z); - } - } - z->progressive = stbi__SOF_progressive(m); - if (!stbi__process_frame_header(z, scan)) return 0; - return 1; -} - -// decode image to YCbCr format -static int stbi__decode_jpeg_image(stbi__jpeg *j) -{ - int m; - for (m = 0; m < 4; m++) { - j->img_comp[m].raw_data = NULL; - j->img_comp[m].raw_coeff = NULL; - } - j->restart_interval = 0; - if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; - m = stbi__get_marker(j); - while (!stbi__EOI(m)) { - if (stbi__SOS(m)) { - if (!stbi__process_scan_header(j)) return 0; - if (!stbi__parse_entropy_coded_data(j)) return 0; - if (j->marker == STBI__MARKER_none ) { - // handle 0s at the end of image data from IP Kamera 9060 - while (!stbi__at_eof(j->s)) { - int x = stbi__get8(j->s); - if (x == 255) { - j->marker = stbi__get8(j->s); - break; - } - } - // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 - } - } else if (stbi__DNL(m)) { - int Ld = stbi__get16be(j->s); - stbi__uint32 NL = stbi__get16be(j->s); - if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); - if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); - } else { - if (!stbi__process_marker(j, m)) return 0; - } - m = stbi__get_marker(j); - } - if (j->progressive) - stbi__jpeg_finish(j); - return 1; -} - -// static jfif-centered resampling (across block boundaries) - -typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, - int w, int hs); - -#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) - -static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - STBI_NOTUSED(out); - STBI_NOTUSED(in_far); - STBI_NOTUSED(w); - STBI_NOTUSED(hs); - return in_near; -} - -static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples vertically for every one in input - int i; - STBI_NOTUSED(hs); - for (i=0; i < w; ++i) - out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); - return out; -} - -static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples horizontally for every one in input - int i; - stbi_uc *input = in_near; - - if (w == 1) { - // if only one sample, can't do any interpolation - out[0] = out[1] = input[0]; - return out; - } - - out[0] = input[0]; - out[1] = stbi__div4(input[0]*3 + input[1] + 2); - for (i=1; i < w-1; ++i) { - int n = 3*input[i]+2; - out[i*2+0] = stbi__div4(n+input[i-1]); - out[i*2+1] = stbi__div4(n+input[i+1]); - } - out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); - out[i*2+1] = input[w-1]; - - STBI_NOTUSED(in_far); - STBI_NOTUSED(hs); - - return out; -} - -#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) - -static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i,t0,t1; - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - out[0] = stbi__div4(t1+2); - for (i=1; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i=0,t0,t1; - - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - // process groups of 8 pixels for as long as we can. - // note we can't handle the last pixel in a row in this loop - // because we need to handle the filter boundary conditions. - for (; i < ((w-1) & ~7); i += 8) { -#if defined(STBI_SSE2) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - __m128i zero = _mm_setzero_si128(); - __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); - __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); - __m128i farw = _mm_unpacklo_epi8(farb, zero); - __m128i nearw = _mm_unpacklo_epi8(nearb, zero); - __m128i diff = _mm_sub_epi16(farw, nearw); - __m128i nears = _mm_slli_epi16(nearw, 2); - __m128i curr = _mm_add_epi16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - __m128i prv0 = _mm_slli_si128(curr, 2); - __m128i nxt0 = _mm_srli_si128(curr, 2); - __m128i prev = _mm_insert_epi16(prv0, t1, 0); - __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - __m128i bias = _mm_set1_epi16(8); - __m128i curs = _mm_slli_epi16(curr, 2); - __m128i prvd = _mm_sub_epi16(prev, curr); - __m128i nxtd = _mm_sub_epi16(next, curr); - __m128i curb = _mm_add_epi16(curs, bias); - __m128i even = _mm_add_epi16(prvd, curb); - __m128i odd = _mm_add_epi16(nxtd, curb); - - // interleave even and odd pixels, then undo scaling. - __m128i int0 = _mm_unpacklo_epi16(even, odd); - __m128i int1 = _mm_unpackhi_epi16(even, odd); - __m128i de0 = _mm_srli_epi16(int0, 4); - __m128i de1 = _mm_srli_epi16(int1, 4); - - // pack and write output - __m128i outv = _mm_packus_epi16(de0, de1); - _mm_storeu_si128((__m128i *) (out + i*2), outv); -#elif defined(STBI_NEON) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - uint8x8_t farb = vld1_u8(in_far + i); - uint8x8_t nearb = vld1_u8(in_near + i); - int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); - int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); - int16x8_t curr = vaddq_s16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - int16x8_t prv0 = vextq_s16(curr, curr, 7); - int16x8_t nxt0 = vextq_s16(curr, curr, 1); - int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); - int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - int16x8_t curs = vshlq_n_s16(curr, 2); - int16x8_t prvd = vsubq_s16(prev, curr); - int16x8_t nxtd = vsubq_s16(next, curr); - int16x8_t even = vaddq_s16(curs, prvd); - int16x8_t odd = vaddq_s16(curs, nxtd); - - // undo scaling and round, then store with even/odd phases interleaved - uint8x8x2_t o; - o.val[0] = vqrshrun_n_s16(even, 4); - o.val[1] = vqrshrun_n_s16(odd, 4); - vst2_u8(out + i*2, o); -#endif - - // "previous" value for next iter - t1 = 3*in_near[i+7] + in_far[i+7]; - } - - t0 = t1; - t1 = 3*in_near[i] + in_far[i]; - out[i*2] = stbi__div16(3*t1 + t0 + 8); - - for (++i; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} -#endif - -static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // resample with nearest-neighbor - int i,j; - STBI_NOTUSED(in_far); - for (i=0; i < w; ++i) - for (j=0; j < hs; ++j) - out[i*hs+j] = in_near[i]; - return out; -} - -// this is a reduced-precision calculation of YCbCr-to-RGB introduced -// to make sure the code produces the same results in both SIMD and scalar -#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) -{ - int i = 0; - -#ifdef STBI_SSE2 - // step == 3 is pretty ugly on the final interleave, and i'm not convinced - // it's useful in practice (you wouldn't use it for textures, for example). - // so just accelerate step == 4 case. - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - __m128i signflip = _mm_set1_epi8(-0x80); - __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); - __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); - __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); - __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); - __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); - __m128i xw = _mm_set1_epi16(255); // alpha channel - - for (; i+7 < count; i += 8) { - // load - __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); - __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); - __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); - __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 - __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 - - // unpack to short (and left-shift cr, cb by 8) - __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); - __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); - __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); - - // color transform - __m128i yws = _mm_srli_epi16(yw, 4); - __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); - __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); - __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); - __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); - __m128i rws = _mm_add_epi16(cr0, yws); - __m128i gwt = _mm_add_epi16(cb0, yws); - __m128i bws = _mm_add_epi16(yws, cb1); - __m128i gws = _mm_add_epi16(gwt, cr1); - - // descale - __m128i rw = _mm_srai_epi16(rws, 4); - __m128i bw = _mm_srai_epi16(bws, 4); - __m128i gw = _mm_srai_epi16(gws, 4); - - // back to byte, set up for transpose - __m128i brb = _mm_packus_epi16(rw, bw); - __m128i gxb = _mm_packus_epi16(gw, xw); - - // transpose to interleave channels - __m128i t0 = _mm_unpacklo_epi8(brb, gxb); - __m128i t1 = _mm_unpackhi_epi8(brb, gxb); - __m128i o0 = _mm_unpacklo_epi16(t0, t1); - __m128i o1 = _mm_unpackhi_epi16(t0, t1); - - // store - _mm_storeu_si128((__m128i *) (out + 0), o0); - _mm_storeu_si128((__m128i *) (out + 16), o1); - out += 32; - } - } -#endif - -#ifdef STBI_NEON - // in this version, step=3 support would be easy to add. but is there demand? - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - uint8x8_t signflip = vdup_n_u8(0x80); - int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); - int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); - int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); - int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); - - for (; i+7 < count; i += 8) { - // load - uint8x8_t y_bytes = vld1_u8(y + i); - uint8x8_t cr_bytes = vld1_u8(pcr + i); - uint8x8_t cb_bytes = vld1_u8(pcb + i); - int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); - int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); - - // expand to s16 - int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); - int16x8_t crw = vshll_n_s8(cr_biased, 7); - int16x8_t cbw = vshll_n_s8(cb_biased, 7); - - // color transform - int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); - int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); - int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); - int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); - int16x8_t rws = vaddq_s16(yws, cr0); - int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); - int16x8_t bws = vaddq_s16(yws, cb1); - - // undo scaling, round, convert to byte - uint8x8x4_t o; - o.val[0] = vqrshrun_n_s16(rws, 4); - o.val[1] = vqrshrun_n_s16(gws, 4); - o.val[2] = vqrshrun_n_s16(bws, 4); - o.val[3] = vdup_n_u8(255); - - // store, interleaving r/g/b/a - vst4_u8(out, o); - out += 8*4; - } - } -#endif - - for (; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} -#endif - -// set up the kernels -static void stbi__setup_jpeg(stbi__jpeg *j) -{ - j->idct_block_kernel = stbi__idct_block; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; - -#ifdef STBI_SSE2 - if (stbi__sse2_available()) { - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; - } -#endif - -#ifdef STBI_NEON - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; -#endif -} - -// clean up the temporary component buffers -static void stbi__cleanup_jpeg(stbi__jpeg *j) -{ - stbi__free_jpeg_components(j, j->s->img_n, 0); -} - -typedef struct -{ - resample_row_func resample; - stbi_uc *line0,*line1; - int hs,vs; // expansion factor in each axis - int w_lores; // horizontal pixels pre-expansion - int ystep; // how far through vertical expansion we are - int ypos; // which pre-expansion row we're on -} stbi__resample; - -// fast 0..255 * 0..255 => 0..255 rounded multiplication -static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) -{ - unsigned int t = x*y + 128; - return (stbi_uc) ((t + (t >>8)) >> 8); -} - -static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) -{ - int n, decode_n, is_rgb; - z->s->img_n = 0; // make stbi__cleanup_jpeg safe - - // validate req_comp - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - - // load a jpeg image from whichever source, but leave in YCbCr format - if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } - - // determine actual number of components to generate - n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; - - is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); - - if (z->s->img_n == 3 && n < 3 && !is_rgb) - decode_n = 1; - else - decode_n = z->s->img_n; - - // resample and color-convert - { - int k; - unsigned int i,j; - stbi_uc *output; - stbi_uc *coutput[4]; - - stbi__resample res_comp[4]; - - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - - // allocate line buffer big enough for upsampling off the edges - // with upsample factor of 4 - z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); - if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - r->hs = z->img_h_max / z->img_comp[k].h; - r->vs = z->img_v_max / z->img_comp[k].v; - r->ystep = r->vs >> 1; - r->w_lores = (z->s->img_x + r->hs-1) / r->hs; - r->ypos = 0; - r->line0 = r->line1 = z->img_comp[k].data; - - if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; - else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; - else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; - else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; - else r->resample = stbi__resample_row_generic; - } - - // can't error after this so, this is safe - output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); - if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - // now go ahead and resample - for (j=0; j < z->s->img_y; ++j) { - stbi_uc *out = output + n * z->s->img_x * j; - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - int y_bot = r->ystep >= (r->vs >> 1); - coutput[k] = r->resample(z->img_comp[k].linebuf, - y_bot ? r->line1 : r->line0, - y_bot ? r->line0 : r->line1, - r->w_lores, r->hs); - if (++r->ystep >= r->vs) { - r->ystep = 0; - r->line0 = r->line1; - if (++r->ypos < z->img_comp[k].y) - r->line1 += z->img_comp[k].w2; - } - } - if (n >= 3) { - stbi_uc *y = coutput[0]; - if (z->s->img_n == 3) { - if (is_rgb) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = y[i]; - out[1] = coutput[1][i]; - out[2] = coutput[2][i]; - out[3] = 255; - out += n; - } - } else { - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else if (z->s->img_n == 4) { - if (z->app14_color_transform == 0) { // CMYK - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(coutput[0][i], m); - out[1] = stbi__blinn_8x8(coutput[1][i], m); - out[2] = stbi__blinn_8x8(coutput[2][i], m); - out[3] = 255; - out += n; - } - } else if (z->app14_color_transform == 2) { // YCCK - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(255 - out[0], m); - out[1] = stbi__blinn_8x8(255 - out[1], m); - out[2] = stbi__blinn_8x8(255 - out[2], m); - out += n; - } - } else { // YCbCr + alpha? Ignore the fourth channel for now - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else - for (i=0; i < z->s->img_x; ++i) { - out[0] = out[1] = out[2] = y[i]; - out[3] = 255; // not used if n==3 - out += n; - } - } else { - if (is_rgb) { - if (n == 1) - for (i=0; i < z->s->img_x; ++i) - *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - else { - for (i=0; i < z->s->img_x; ++i, out += 2) { - out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - out[1] = 255; - } - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); - stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); - stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); - out[0] = stbi__compute_y(r, g, b); - out[1] = 255; - out += n; - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); - out[1] = 255; - out += n; - } - } else { - stbi_uc *y = coutput[0]; - if (n == 1) - for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; - else - for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; - } - } - } - stbi__cleanup_jpeg(z); - *out_x = z->s->img_x; - *out_y = z->s->img_y; - if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output - return output; - } -} - -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - unsigned char* result; - stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); - STBI_NOTUSED(ri); - j->s = s; - stbi__setup_jpeg(j); - result = load_jpeg_image(j, x,y,comp,req_comp); - STBI_FREE(j); - return result; -} - -static int stbi__jpeg_test(stbi__context *s) -{ - int r; - stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); - j->s = s; - stbi__setup_jpeg(j); - r = stbi__decode_jpeg_header(j, STBI__SCAN_type); - stbi__rewind(s); - STBI_FREE(j); - return r; -} - -static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) -{ - if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { - stbi__rewind( j->s ); - return 0; - } - if (x) *x = j->s->img_x; - if (y) *y = j->s->img_y; - if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; - return 1; -} - -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) -{ - int result; - stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); - j->s = s; - result = stbi__jpeg_info_raw(j, x, y, comp); - STBI_FREE(j); - return result; -} -#endif - -// public domain zlib decode v0.2 Sean Barrett 2006-11-18 -// simple implementation -// - all input must be provided in an upfront buffer -// - all output is written to a single output buffer (can malloc/realloc) -// performance -// - fast huffman - -#ifndef STBI_NO_ZLIB - -// fast-way is faster to check than jpeg huffman, but slow way is slower -#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables -#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) - -// zlib-style huffman encoding -// (jpegs packs from left, zlib from right, so can't share code) -typedef struct -{ - stbi__uint16 fast[1 << STBI__ZFAST_BITS]; - stbi__uint16 firstcode[16]; - int maxcode[17]; - stbi__uint16 firstsymbol[16]; - stbi_uc size[288]; - stbi__uint16 value[288]; -} stbi__zhuffman; - -stbi_inline static int stbi__bitreverse16(int n) -{ - n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); - n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); - n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); - n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); - return n; -} - -stbi_inline static int stbi__bit_reverse(int v, int bits) -{ - STBI_ASSERT(bits <= 16); - // to bit reverse n bits, reverse 16 and shift - // e.g. 11 bits, bit reverse and shift away 5 - return stbi__bitreverse16(v) >> (16-bits); -} - -static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) -{ - int i,k=0; - int code, next_code[16], sizes[17]; - - // DEFLATE spec for generating codes - memset(sizes, 0, sizeof(sizes)); - memset(z->fast, 0, sizeof(z->fast)); - for (i=0; i < num; ++i) - ++sizes[sizelist[i]]; - sizes[0] = 0; - for (i=1; i < 16; ++i) - if (sizes[i] > (1 << i)) - return stbi__err("bad sizes", "Corrupt PNG"); - code = 0; - for (i=1; i < 16; ++i) { - next_code[i] = code; - z->firstcode[i] = (stbi__uint16) code; - z->firstsymbol[i] = (stbi__uint16) k; - code = (code + sizes[i]); - if (sizes[i]) - if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); - z->maxcode[i] = code << (16-i); // preshift for inner loop - code <<= 1; - k += sizes[i]; - } - z->maxcode[16] = 0x10000; // sentinel - for (i=0; i < num; ++i) { - int s = sizelist[i]; - if (s) { - int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; - stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); - z->size [c] = (stbi_uc ) s; - z->value[c] = (stbi__uint16) i; - if (s <= STBI__ZFAST_BITS) { - int j = stbi__bit_reverse(next_code[s],s); - while (j < (1 << STBI__ZFAST_BITS)) { - z->fast[j] = fastv; - j += (1 << s); - } - } - ++next_code[s]; - } - } - return 1; -} - -// zlib-from-memory implementation for PNG reading -// because PNG allows splitting the zlib stream arbitrarily, -// and it's annoying structurally to have PNG call ZLIB call PNG, -// we require PNG read all the IDATs and combine them into a single -// memory buffer - -typedef struct -{ - stbi_uc *zbuffer, *zbuffer_end; - int num_bits; - stbi__uint32 code_buffer; - - char *zout; - char *zout_start; - char *zout_end; - int z_expandable; - - stbi__zhuffman z_length, z_distance; -} stbi__zbuf; - -stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) -{ - if (z->zbuffer >= z->zbuffer_end) return 0; - return *z->zbuffer++; -} - -static void stbi__fill_bits(stbi__zbuf *z) -{ - do { - STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); - z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; - z->num_bits += 8; - } while (z->num_bits <= 24); -} - -stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) -{ - unsigned int k; - if (z->num_bits < n) stbi__fill_bits(z); - k = z->code_buffer & ((1 << n) - 1); - z->code_buffer >>= n; - z->num_bits -= n; - return k; -} - -static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s,k; - // not resolved by fast table, so compute it the slow way - // use jpeg approach, which requires MSbits at top - k = stbi__bit_reverse(a->code_buffer, 16); - for (s=STBI__ZFAST_BITS+1; ; ++s) - if (k < z->maxcode[s]) - break; - if (s == 16) return -1; // invalid code! - // code size is s, so: - b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - STBI_ASSERT(z->size[b] == s); - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; -} - -stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s; - if (a->num_bits < 16) stbi__fill_bits(a); - b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; - if (b) { - s = b >> 9; - a->code_buffer >>= s; - a->num_bits -= s; - return b & 511; - } - return stbi__zhuffman_decode_slowpath(a, z); -} - -static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes -{ - char *q; - int cur, limit, old_limit; - z->zout = zout; - if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); - cur = (int) (z->zout - z->zout_start); - limit = old_limit = (int) (z->zout_end - z->zout_start); - while (cur + n > limit) - limit *= 2; - q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); - STBI_NOTUSED(old_limit); - if (q == NULL) return stbi__err("outofmem", "Out of memory"); - z->zout_start = q; - z->zout = q + cur; - z->zout_end = q + limit; - return 1; -} - -static const int stbi__zlength_base[31] = { - 3,4,5,6,7,8,9,10,11,13, - 15,17,19,23,27,31,35,43,51,59, - 67,83,99,115,131,163,195,227,258,0,0 }; - -static const int stbi__zlength_extra[31]= -{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; - -static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, -257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; - -static const int stbi__zdist_extra[32] = -{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; - -static int stbi__parse_huffman_block(stbi__zbuf *a) -{ - char *zout = a->zout; - for(;;) { - int z = stbi__zhuffman_decode(a, &a->z_length); - if (z < 256) { - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes - if (zout >= a->zout_end) { - if (!stbi__zexpand(a, zout, 1)) return 0; - zout = a->zout; - } - *zout++ = (char) z; - } else { - stbi_uc *p; - int len,dist; - if (z == 256) { - a->zout = zout; - return 1; - } - z -= 257; - len = stbi__zlength_base[z]; - if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); - z = stbi__zhuffman_decode(a, &a->z_distance); - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); - dist = stbi__zdist_base[z]; - if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); - if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); - if (zout + len > a->zout_end) { - if (!stbi__zexpand(a, zout, len)) return 0; - zout = a->zout; - } - p = (stbi_uc *) (zout - dist); - if (dist == 1) { // run of one byte; common in images. - stbi_uc v = *p; - if (len) { do *zout++ = v; while (--len); } - } else { - if (len) { do *zout++ = *p++; while (--len); } - } - } - } -} - -static int stbi__compute_huffman_codes(stbi__zbuf *a) -{ - static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - stbi__zhuffman z_codelength; - stbi_uc lencodes[286+32+137];//padding for maximum single op - stbi_uc codelength_sizes[19]; - int i,n; - - int hlit = stbi__zreceive(a,5) + 257; - int hdist = stbi__zreceive(a,5) + 1; - int hclen = stbi__zreceive(a,4) + 4; - int ntot = hlit + hdist; - - memset(codelength_sizes, 0, sizeof(codelength_sizes)); - for (i=0; i < hclen; ++i) { - int s = stbi__zreceive(a,3); - codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; - } - if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; - - n = 0; - while (n < ntot) { - int c = stbi__zhuffman_decode(a, &z_codelength); - if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); - if (c < 16) - lencodes[n++] = (stbi_uc) c; - else { - stbi_uc fill = 0; - if (c == 16) { - c = stbi__zreceive(a,2)+3; - if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); - fill = lencodes[n-1]; - } else if (c == 17) - c = stbi__zreceive(a,3)+3; - else { - STBI_ASSERT(c == 18); - c = stbi__zreceive(a,7)+11; - } - if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); - memset(lencodes+n, fill, c); - n += c; - } - } - if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); - if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; - return 1; -} - -static int stbi__parse_uncompressed_block(stbi__zbuf *a) -{ - stbi_uc header[4]; - int len,nlen,k; - if (a->num_bits & 7) - stbi__zreceive(a, a->num_bits & 7); // discard - // drain the bit-packed data into header - k = 0; - while (a->num_bits > 0) { - header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check - a->code_buffer >>= 8; - a->num_bits -= 8; - } - STBI_ASSERT(a->num_bits == 0); - // now fill header the normal way - while (k < 4) - header[k++] = stbi__zget8(a); - len = header[1] * 256 + header[0]; - nlen = header[3] * 256 + header[2]; - if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); - if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); - if (a->zout + len > a->zout_end) - if (!stbi__zexpand(a, a->zout, len)) return 0; - memcpy(a->zout, a->zbuffer, len); - a->zbuffer += len; - a->zout += len; - return 1; -} - -static int stbi__parse_zlib_header(stbi__zbuf *a) -{ - int cmf = stbi__zget8(a); - int cm = cmf & 15; - /* int cinfo = cmf >> 4; */ - int flg = stbi__zget8(a); - if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png - if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png - // window = 1 << (8 + cinfo)... but who cares, we fully buffer output - return 1; -} - -static const stbi_uc stbi__zdefault_length[288] = -{ - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 -}; -static const stbi_uc stbi__zdefault_distance[32] = -{ - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 -}; -/* -Init algorithm: -{ - int i; // use <= to match clearly with spec - for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; - for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; - for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; - for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; - - for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; -} -*/ - -static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) -{ - int final, type; - if (parse_header) - if (!stbi__parse_zlib_header(a)) return 0; - a->num_bits = 0; - a->code_buffer = 0; - do { - final = stbi__zreceive(a,1); - type = stbi__zreceive(a,2); - if (type == 0) { - if (!stbi__parse_uncompressed_block(a)) return 0; - } else if (type == 3) { - return 0; - } else { - if (type == 1) { - // use fixed code lengths - if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; - } else { - if (!stbi__compute_huffman_codes(a)) return 0; - } - if (!stbi__parse_huffman_block(a)) return 0; - } - } while (!final); - return 1; -} - -static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) -{ - a->zout_start = obuf; - a->zout = obuf; - a->zout_end = obuf + olen; - a->z_expandable = exp; - - return stbi__parse_zlib(a, parse_header); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) -{ - return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) - return (int) (a.zout - a.zout_start); - else - return -1; -} - -STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(16384); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer+len; - if (stbi__do_zlib(&a, p, 16384, 1, 0)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) - return (int) (a.zout - a.zout_start); - else - return -1; -} -#endif - -// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 -// simple implementation -// - only 8-bit samples -// - no CRC checking -// - allocates lots of intermediate memory -// - avoids problem of streaming data between subsystems -// - avoids explicit window management -// performance -// - uses stb_zlib, a PD zlib implementation with fast huffman decoding - -#ifndef STBI_NO_PNG -typedef struct -{ - stbi__uint32 length; - stbi__uint32 type; -} stbi__pngchunk; - -static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) -{ - stbi__pngchunk c; - c.length = stbi__get32be(s); - c.type = stbi__get32be(s); - return c; -} - -static int stbi__check_png_header(stbi__context *s) -{ - static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; - int i; - for (i=0; i < 8; ++i) - if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); - return 1; -} - -typedef struct -{ - stbi__context *s; - stbi_uc *idata, *expanded, *out; - int depth; -} stbi__png; - - -enum { - STBI__F_none=0, - STBI__F_sub=1, - STBI__F_up=2, - STBI__F_avg=3, - STBI__F_paeth=4, - // synthetic filters used for first scanline to avoid needing a dummy row of 0s - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static stbi_uc first_row_filter[5] = -{ - STBI__F_none, - STBI__F_sub, - STBI__F_none, - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static int stbi__paeth(int a, int b, int c) -{ - int p = a + b - c; - int pa = abs(p-a); - int pb = abs(p-b); - int pc = abs(p-c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; -} - -static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; - -// create the png data from post-deflated data -static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) -{ - int bytes = (depth == 16? 2 : 1); - stbi__context *s = a->s; - stbi__uint32 i,j,stride = x*out_n*bytes; - stbi__uint32 img_len, img_width_bytes; - int k; - int img_n = s->img_n; // copy it into a local for later - - int output_bytes = out_n*bytes; - int filter_bytes = img_n*bytes; - int width = x; - - STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); - a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into - if (!a->out) return stbi__err("outofmem", "Out of memory"); - - if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); - img_width_bytes = (((img_n * x * depth) + 7) >> 3); - img_len = (img_width_bytes + 1) * y; - - // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, - // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), - // so just check for raw_len < img_len always. - if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); - - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *prior; - int filter = *raw++; - - if (filter > 4) - return stbi__err("invalid filter","Corrupt PNG"); - - if (depth < 8) { - STBI_ASSERT(img_width_bytes <= x); - cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place - filter_bytes = 1; - width = img_width_bytes; - } - prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above - - // if first row, use special filter that doesn't sample previous row - if (j == 0) filter = first_row_filter[filter]; - - // handle first byte explicitly - for (k=0; k < filter_bytes; ++k) { - switch (filter) { - case STBI__F_none : cur[k] = raw[k]; break; - case STBI__F_sub : cur[k] = raw[k]; break; - case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; - case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; - case STBI__F_avg_first : cur[k] = raw[k]; break; - case STBI__F_paeth_first: cur[k] = raw[k]; break; - } - } - - if (depth == 8) { - if (img_n != out_n) - cur[img_n] = 255; // first pixel - raw += img_n; - cur += out_n; - prior += out_n; - } else if (depth == 16) { - if (img_n != out_n) { - cur[filter_bytes] = 255; // first pixel top byte - cur[filter_bytes+1] = 255; // first pixel bottom byte - } - raw += filter_bytes; - cur += output_bytes; - prior += output_bytes; - } else { - raw += 1; - cur += 1; - prior += 1; - } - - // this is a little gross, so that we don't switch per-pixel or per-component - if (depth < 8 || img_n == out_n) { - int nk = (width - 1)*filter_bytes; - #define STBI__CASE(f) \ - case f: \ - for (k=0; k < nk; ++k) - switch (filter) { - // "none" filter turns into a memcpy here; make that explicit. - case STBI__F_none: memcpy(cur, raw, nk); break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; - } - #undef STBI__CASE - raw += nk; - } else { - STBI_ASSERT(img_n+1 == out_n); - #define STBI__CASE(f) \ - case f: \ - for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ - for (k=0; k < filter_bytes; ++k) - switch (filter) { - STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; - } - #undef STBI__CASE - - // the loop above sets the high byte of the pixels' alpha, but for - // 16 bit png files we also need the low byte set. we'll do that here. - if (depth == 16) { - cur = a->out + stride*j; // start at the beginning of the row again - for (i=0; i < x; ++i,cur+=output_bytes) { - cur[filter_bytes+1] = 255; - } - } - } - } - - // we make a separate pass to expand bits to pixels; for performance, - // this could run two scanlines behind the above code, so it won't - // intefere with filtering but will still be in the cache. - if (depth < 8) { - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; - // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit - // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop - stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range - - // note that the final byte might overshoot and write more data than desired. - // we can allocate enough data that this never writes out of memory, but it - // could also overwrite the next scanline. can it overwrite non-empty data - // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. - // so we need to explicitly clamp the final ones - - if (depth == 4) { - for (k=x*img_n; k >= 2; k-=2, ++in) { - *cur++ = scale * ((*in >> 4) ); - *cur++ = scale * ((*in ) & 0x0f); - } - if (k > 0) *cur++ = scale * ((*in >> 4) ); - } else if (depth == 2) { - for (k=x*img_n; k >= 4; k-=4, ++in) { - *cur++ = scale * ((*in >> 6) ); - *cur++ = scale * ((*in >> 4) & 0x03); - *cur++ = scale * ((*in >> 2) & 0x03); - *cur++ = scale * ((*in ) & 0x03); - } - if (k > 0) *cur++ = scale * ((*in >> 6) ); - if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); - if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); - } else if (depth == 1) { - for (k=x*img_n; k >= 8; k-=8, ++in) { - *cur++ = scale * ((*in >> 7) ); - *cur++ = scale * ((*in >> 6) & 0x01); - *cur++ = scale * ((*in >> 5) & 0x01); - *cur++ = scale * ((*in >> 4) & 0x01); - *cur++ = scale * ((*in >> 3) & 0x01); - *cur++ = scale * ((*in >> 2) & 0x01); - *cur++ = scale * ((*in >> 1) & 0x01); - *cur++ = scale * ((*in ) & 0x01); - } - if (k > 0) *cur++ = scale * ((*in >> 7) ); - if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); - if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); - if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); - if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); - if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); - if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); - } - if (img_n != out_n) { - int q; - // insert alpha = 255 - cur = a->out + stride*j; - if (img_n == 1) { - for (q=x-1; q >= 0; --q) { - cur[q*2+1] = 255; - cur[q*2+0] = cur[q]; - } - } else { - STBI_ASSERT(img_n == 3); - for (q=x-1; q >= 0; --q) { - cur[q*4+3] = 255; - cur[q*4+2] = cur[q*3+2]; - cur[q*4+1] = cur[q*3+1]; - cur[q*4+0] = cur[q*3+0]; - } - } - } - } - } else if (depth == 16) { - // force the image data from big-endian to platform-native. - // this is done in a separate pass due to the decoding relying - // on the data being untouched, but could probably be done - // per-line during decode if care is taken. - stbi_uc *cur = a->out; - stbi__uint16 *cur16 = (stbi__uint16*)cur; - - for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { - *cur16 = (cur[0] << 8) | cur[1]; - } - } - - return 1; -} - -static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) -{ - int bytes = (depth == 16 ? 2 : 1); - int out_bytes = out_n * bytes; - stbi_uc *final; - int p; - if (!interlaced) - return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); - - // de-interlacing - final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); - for (p=0; p < 7; ++p) { - int xorig[] = { 0,4,0,2,0,1,0 }; - int yorig[] = { 0,0,4,0,2,0,1 }; - int xspc[] = { 8,8,4,4,2,2,1 }; - int yspc[] = { 8,8,8,4,4,2,2 }; - int i,j,x,y; - // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 - x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; - y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; - if (x && y) { - stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; - if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { - STBI_FREE(final); - return 0; - } - for (j=0; j < y; ++j) { - for (i=0; i < x; ++i) { - int out_y = j*yspc[p]+yorig[p]; - int out_x = i*xspc[p]+xorig[p]; - memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, - a->out + (j*x+i)*out_bytes, out_bytes); - } - } - STBI_FREE(a->out); - image_data += img_len; - image_data_len -= img_len; - } - } - a->out = final; - - return 1; -} - -static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - // compute color-based transparency, assuming we've - // already got 255 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i=0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 255); - p += 2; - } - } else { - for (i=0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi__uint16 *p = (stbi__uint16*) z->out; - - // compute color-based transparency, assuming we've - // already got 65535 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i = 0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 65535); - p += 2; - } - } else { - for (i = 0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) -{ - stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; - stbi_uc *p, *temp_out, *orig = a->out; - - p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); - if (p == NULL) return stbi__err("outofmem", "Out of memory"); - - // between here and free(out) below, exitting would leak - temp_out = p; - - if (pal_img_n == 3) { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p += 3; - } - } else { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p[3] = palette[n+3]; - p += 4; - } - } - STBI_FREE(a->out); - a->out = temp_out; - - STBI_NOTUSED(len); - - return 1; -} - -static int stbi__unpremultiply_on_load = 0; -static int stbi__de_iphone_flag = 0; - -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) -{ - stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; -} - -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) -{ - stbi__de_iphone_flag = flag_true_if_should_convert; -} - -static void stbi__de_iphone(stbi__png *z) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - if (s->img_out_n == 3) { // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 3; - } - } else { - STBI_ASSERT(s->img_out_n == 4); - if (stbi__unpremultiply_on_load) { - // convert bgr to rgb and unpremultiply - for (i=0; i < pixel_count; ++i) { - stbi_uc a = p[3]; - stbi_uc t = p[0]; - if (a) { - stbi_uc half = a / 2; - p[0] = (p[2] * 255 + half) / a; - p[1] = (p[1] * 255 + half) / a; - p[2] = ( t * 255 + half) / a; - } else { - p[0] = p[2]; - p[2] = t; - } - p += 4; - } - } else { - // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 4; - } - } - } -} - -#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) - -static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) -{ - stbi_uc palette[1024], pal_img_n=0; - stbi_uc has_trans=0, tc[3]; - stbi__uint16 tc16[3]; - stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; - int first=1,k,interlace=0, color=0, is_iphone=0; - stbi__context *s = z->s; - - z->expanded = NULL; - z->idata = NULL; - z->out = NULL; - - if (!stbi__check_png_header(s)) return 0; - - if (scan == STBI__SCAN_type) return 1; - - for (;;) { - stbi__pngchunk c = stbi__get_chunk_header(s); - switch (c.type) { - case STBI__PNG_TYPE('C','g','B','I'): - is_iphone = 1; - stbi__skip(s, c.length); - break; - case STBI__PNG_TYPE('I','H','D','R'): { - int comp,filter; - if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); - first = 0; - if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); - s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); - s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); - z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); - color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); - comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); - filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); - interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); - if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); - if (!pal_img_n) { - s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - if (scan == STBI__SCAN_header) return 1; - } else { - // if paletted, then pal_n is our final components, and - // img_n is # components to decompress/filter. - s->img_n = 1; - if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); - // if SCAN_header, have to scan to see if we have a tRNS - } - break; - } - - case STBI__PNG_TYPE('P','L','T','E'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); - for (i=0; i < pal_len; ++i) { - palette[i*4+0] = stbi__get8(s); - palette[i*4+1] = stbi__get8(s); - palette[i*4+2] = stbi__get8(s); - palette[i*4+3] = 255; - } - break; - } - - case STBI__PNG_TYPE('t','R','N','S'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); - if (pal_img_n) { - if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } - if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); - if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); - pal_img_n = 4; - for (i=0; i < c.length; ++i) - palette[i*4+3] = stbi__get8(s); - } else { - if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); - if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); - has_trans = 1; - if (z->depth == 16) { - for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is - } else { - for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger - } - } - break; - } - - case STBI__PNG_TYPE('I','D','A','T'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); - if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } - if ((int)(ioff + c.length) < (int)ioff) return 0; - if (ioff + c.length > idata_limit) { - stbi__uint32 idata_limit_old = idata_limit; - stbi_uc *p; - if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - STBI_NOTUSED(idata_limit_old); - p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); - z->idata = p; - } - if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); - ioff += c.length; - break; - } - - case STBI__PNG_TYPE('I','E','N','D'): { - stbi__uint32 raw_len, bpl; - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (scan != STBI__SCAN_load) return 1; - if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); - // initial guess for decoded data size to avoid unnecessary reallocs - bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component - raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; - z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); - if (z->expanded == NULL) return 0; // zlib should set error - STBI_FREE(z->idata); z->idata = NULL; - if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) - s->img_out_n = s->img_n+1; - else - s->img_out_n = s->img_n; - if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; - if (has_trans) { - if (z->depth == 16) { - if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; - } else { - if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; - } - } - if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) - stbi__de_iphone(z); - if (pal_img_n) { - // pal_img_n == 3 or 4 - s->img_n = pal_img_n; // record the actual colors we had - s->img_out_n = pal_img_n; - if (req_comp >= 3) s->img_out_n = req_comp; - if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) - return 0; - } else if (has_trans) { - // non-paletted image with tRNS -> source image has (constant) alpha - ++s->img_n; - } - STBI_FREE(z->expanded); z->expanded = NULL; - return 1; - } - - default: - // if critical, fail - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if ((c.type & (1 << 29)) == 0) { - #ifndef STBI_NO_FAILURE_STRINGS - // not threadsafe - static char invalid_chunk[] = "XXXX PNG chunk not known"; - invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); - invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); - invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); - invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); - #endif - return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); - } - stbi__skip(s, c.length); - break; - } - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - } -} - -static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) -{ - void *result=NULL; - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth < 8) - ri->bits_per_channel = 8; - else - ri->bits_per_channel = p->depth; - result = p->out; - p->out = NULL; - if (req_comp && req_comp != p->s->img_out_n) { - if (ri->bits_per_channel == 8) - result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - else - result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - p->s->img_out_n = req_comp; - if (result == NULL) return result; - } - *x = p->s->img_x; - *y = p->s->img_y; - if (n) *n = p->s->img_n; - } - STBI_FREE(p->out); p->out = NULL; - STBI_FREE(p->expanded); p->expanded = NULL; - STBI_FREE(p->idata); p->idata = NULL; - - return result; -} - -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi__png p; - p.s = s; - return stbi__do_png(&p, x,y,comp,req_comp, ri); -} - -static int stbi__png_test(stbi__context *s) -{ - int r; - r = stbi__check_png_header(s); - stbi__rewind(s); - return r; -} - -static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) -{ - if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { - stbi__rewind( p->s ); - return 0; - } - if (x) *x = p->s->img_x; - if (y) *y = p->s->img_y; - if (comp) *comp = p->s->img_n; - return 1; -} - -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__png p; - p.s = s; - return stbi__png_info_raw(&p, x, y, comp); -} - -static int stbi__png_is16(stbi__context *s) -{ - stbi__png p; - p.s = s; - if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) - return 0; - if (p.depth != 16) { - stbi__rewind(p.s); - return 0; - } - return 1; -} -#endif - -// Microsoft/Windows BMP image - -#ifndef STBI_NO_BMP -static int stbi__bmp_test_raw(stbi__context *s) -{ - int r; - int sz; - if (stbi__get8(s) != 'B') return 0; - if (stbi__get8(s) != 'M') return 0; - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - stbi__get32le(s); // discard data offset - sz = stbi__get32le(s); - r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); - return r; -} - -static int stbi__bmp_test(stbi__context *s) -{ - int r = stbi__bmp_test_raw(s); - stbi__rewind(s); - return r; -} - - -// returns 0..31 for the highest set bit -static int stbi__high_bit(unsigned int z) -{ - int n=0; - if (z == 0) return -1; - if (z >= 0x10000) n += 16, z >>= 16; - if (z >= 0x00100) n += 8, z >>= 8; - if (z >= 0x00010) n += 4, z >>= 4; - if (z >= 0x00004) n += 2, z >>= 2; - if (z >= 0x00002) n += 1, z >>= 1; - return n; -} - -static int stbi__bitcount(unsigned int a) -{ - a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 - a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits - a = (a + (a >> 8)); // max 16 per 8 bits - a = (a + (a >> 16)); // max 32 per 8 bits - return a & 0xff; -} - -// extract an arbitrarily-aligned N-bit value (N=bits) -// from v, and then make it 8-bits long and fractionally -// extend it to full full range. -static int stbi__shiftsigned(int v, int shift, int bits) -{ - static unsigned int mul_table[9] = { - 0, - 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, - 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, - }; - static unsigned int shift_table[9] = { - 0, 0,0,1,0,2,4,6,0, - }; - if (shift < 0) - v <<= -shift; - else - v >>= shift; - STBI_ASSERT(v >= 0 && v < 256); - v >>= (8-bits); - STBI_ASSERT(bits >= 0 && bits <= 8); - return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; -} - -typedef struct -{ - int bpp, offset, hsz; - unsigned int mr,mg,mb,ma, all_a; -} stbi__bmp_data; - -static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) -{ - int hsz; - if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - info->offset = stbi__get32le(s); - info->hsz = hsz = stbi__get32le(s); - info->mr = info->mg = info->mb = info->ma = 0; - - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); - if (hsz == 12) { - s->img_x = stbi__get16le(s); - s->img_y = stbi__get16le(s); - } else { - s->img_x = stbi__get32le(s); - s->img_y = stbi__get32le(s); - } - if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); - info->bpp = stbi__get16le(s); - if (hsz != 12) { - int compress = stbi__get32le(s); - if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); - stbi__get32le(s); // discard sizeof - stbi__get32le(s); // discard hres - stbi__get32le(s); // discard vres - stbi__get32le(s); // discard colorsused - stbi__get32le(s); // discard max important - if (hsz == 40 || hsz == 56) { - if (hsz == 56) { - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - } - if (info->bpp == 16 || info->bpp == 32) { - if (compress == 0) { - if (info->bpp == 32) { - info->mr = 0xffu << 16; - info->mg = 0xffu << 8; - info->mb = 0xffu << 0; - info->ma = 0xffu << 24; - info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 - } else { - info->mr = 31u << 10; - info->mg = 31u << 5; - info->mb = 31u << 0; - } - } else if (compress == 3) { - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - // not documented, but generated by photoshop and handled by mspaint - if (info->mr == info->mg && info->mg == info->mb) { - // ?!?!? - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else { - int i; - if (hsz != 108 && hsz != 124) - return stbi__errpuc("bad BMP", "bad BMP"); - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->ma = stbi__get32le(s); - stbi__get32le(s); // discard color space - for (i=0; i < 12; ++i) - stbi__get32le(s); // discard color space parameters - if (hsz == 124) { - stbi__get32le(s); // discard rendering intent - stbi__get32le(s); // discard offset of profile data - stbi__get32le(s); // discard size of profile data - stbi__get32le(s); // discard reserved - } - } - } - return (void *) 1; -} - - -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - unsigned int mr=0,mg=0,mb=0,ma=0, all_a; - stbi_uc pal[256][4]; - int psize=0,i,j,width; - int flip_vertically, pad, target; - stbi__bmp_data info; - STBI_NOTUSED(ri); - - info.all_a = 255; - if (stbi__bmp_parse_header(s, &info) == NULL) - return NULL; // error code already set - - flip_vertically = ((int) s->img_y) > 0; - s->img_y = abs((int) s->img_y); - - mr = info.mr; - mg = info.mg; - mb = info.mb; - ma = info.ma; - all_a = info.all_a; - - if (info.hsz == 12) { - if (info.bpp < 24) - psize = (info.offset - 14 - 24) / 3; - } else { - if (info.bpp < 16) - psize = (info.offset - 14 - info.hsz) >> 2; - } - - s->img_n = ma ? 4 : 3; - if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 - target = req_comp; - else - target = s->img_n; // if they want monochrome, we'll post-convert - - // sanity-check size - if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "Corrupt BMP"); - - out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - if (info.bpp < 16) { - int z=0; - if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } - for (i=0; i < psize; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - if (info.hsz != 12) stbi__get8(s); - pal[i][3] = 255; - } - stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); - if (info.bpp == 1) width = (s->img_x + 7) >> 3; - else if (info.bpp == 4) width = (s->img_x + 1) >> 1; - else if (info.bpp == 8) width = s->img_x; - else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } - pad = (-width)&3; - if (info.bpp == 1) { - for (j=0; j < (int) s->img_y; ++j) { - int bit_offset = 7, v = stbi__get8(s); - for (i=0; i < (int) s->img_x; ++i) { - int color = (v>>bit_offset)&0x1; - out[z++] = pal[color][0]; - out[z++] = pal[color][1]; - out[z++] = pal[color][2]; - if((--bit_offset) < 0) { - bit_offset = 7; - v = stbi__get8(s); - } - } - stbi__skip(s, pad); - } - } else { - for (j=0; j < (int) s->img_y; ++j) { - for (i=0; i < (int) s->img_x; i += 2) { - int v=stbi__get8(s),v2=0; - if (info.bpp == 4) { - v2 = v & 15; - v >>= 4; - } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - v = (info.bpp == 8) ? stbi__get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - } - stbi__skip(s, pad); - } - } - } else { - int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; - int z = 0; - int easy=0; - stbi__skip(s, info.offset - 14 - info.hsz); - if (info.bpp == 24) width = 3 * s->img_x; - else if (info.bpp == 16) width = 2*s->img_x; - else /* bpp = 32 and pad = 0 */ width=0; - pad = (-width) & 3; - if (info.bpp == 24) { - easy = 1; - } else if (info.bpp == 32) { - if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) - easy = 2; - } - if (!easy) { - if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - // right shift amt to put high bit in position #7 - rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); - gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); - bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); - ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); - } - for (j=0; j < (int) s->img_y; ++j) { - if (easy) { - for (i=0; i < (int) s->img_x; ++i) { - unsigned char a; - out[z+2] = stbi__get8(s); - out[z+1] = stbi__get8(s); - out[z+0] = stbi__get8(s); - z += 3; - a = (easy == 2 ? stbi__get8(s) : 255); - all_a |= a; - if (target == 4) out[z++] = a; - } - } else { - int bpp = info.bpp; - for (i=0; i < (int) s->img_x; ++i) { - stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); - unsigned int a; - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); - a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); - all_a |= a; - if (target == 4) out[z++] = STBI__BYTECAST(a); - } - } - stbi__skip(s, pad); - } - } - - // if alpha channel is all 0s, replace with all 255s - if (target == 4 && all_a == 0) - for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) - out[i] = 255; - - if (flip_vertically) { - stbi_uc t; - for (j=0; j < (int) s->img_y>>1; ++j) { - stbi_uc *p1 = out + j *s->img_x*target; - stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; - for (i=0; i < (int) s->img_x*target; ++i) { - t = p1[i], p1[i] = p2[i], p2[i] = t; - } - } - } - - if (req_comp && req_comp != target) { - out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - return out; -} -#endif - -// Targa Truevision - TGA -// by Jonathan Dummer -#ifndef STBI_NO_TGA -// returns STBI_rgb or whatever, 0 on error -static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) -{ - // only RGB or RGBA (incl. 16bit) or grey allowed - if (is_rgb16) *is_rgb16 = 0; - switch(bits_per_pixel) { - case 8: return STBI_grey; - case 16: if(is_grey) return STBI_grey_alpha; - // fallthrough - case 15: if(is_rgb16) *is_rgb16 = 1; - return STBI_rgb; - case 24: // fallthrough - case 32: return bits_per_pixel/8; - default: return 0; - } -} - -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) -{ - int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; - int sz, tga_colormap_type; - stbi__get8(s); // discard Offset - tga_colormap_type = stbi__get8(s); // colormap type - if( tga_colormap_type > 1 ) { - stbi__rewind(s); - return 0; // only RGB or indexed allowed - } - tga_image_type = stbi__get8(s); // image type - if ( tga_colormap_type == 1 ) { // colormapped (paletted) image - if (tga_image_type != 1 && tga_image_type != 9) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip image x and y origin - tga_colormap_bpp = sz; - } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE - if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { - stbi__rewind(s); - return 0; // only RGB or grey allowed, +/- RLE - } - stbi__skip(s,9); // skip colormap specification and image x/y origin - tga_colormap_bpp = 0; - } - tga_w = stbi__get16le(s); - if( tga_w < 1 ) { - stbi__rewind(s); - return 0; // test width - } - tga_h = stbi__get16le(s); - if( tga_h < 1 ) { - stbi__rewind(s); - return 0; // test height - } - tga_bits_per_pixel = stbi__get8(s); // bits per pixel - stbi__get8(s); // ignore alpha bits - if (tga_colormap_bpp != 0) { - if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { - // when using a colormap, tga_bits_per_pixel is the size of the indexes - // I don't think anything but 8 or 16bit indexes makes sense - stbi__rewind(s); - return 0; - } - tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); - } else { - tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); - } - if(!tga_comp) { - stbi__rewind(s); - return 0; - } - if (x) *x = tga_w; - if (y) *y = tga_h; - if (comp) *comp = tga_comp; - return 1; // seems to have passed everything -} - -static int stbi__tga_test(stbi__context *s) -{ - int res = 0; - int sz, tga_color_type; - stbi__get8(s); // discard Offset - tga_color_type = stbi__get8(s); // color type - if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed - sz = stbi__get8(s); // image type - if ( tga_color_type == 1 ) { // colormapped (paletted) image - if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - stbi__skip(s,4); // skip image x and y origin - } else { // "normal" image w/o colormap - if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE - stbi__skip(s,9); // skip colormap specification and image x/y origin - } - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height - sz = stbi__get8(s); // bits per pixel - if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - - res = 1; // if we got this far, everything's good and we can return 1 instead of 0 - -errorEnd: - stbi__rewind(s); - return res; -} - -// read 16bit value and convert to 24bit RGB -static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) -{ - stbi__uint16 px = (stbi__uint16)stbi__get16le(s); - stbi__uint16 fiveBitMask = 31; - // we have 3 channels with 5bits each - int r = (px >> 10) & fiveBitMask; - int g = (px >> 5) & fiveBitMask; - int b = px & fiveBitMask; - // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later - out[0] = (stbi_uc)((r * 255)/31); - out[1] = (stbi_uc)((g * 255)/31); - out[2] = (stbi_uc)((b * 255)/31); - - // some people claim that the most significant bit might be used for alpha - // (possibly if an alpha-bit is set in the "image descriptor byte") - // but that only made 16bit test images completely translucent.. - // so let's treat all 15 and 16bit TGAs as RGB with no alpha. -} - -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - // read in the TGA header stuff - int tga_offset = stbi__get8(s); - int tga_indexed = stbi__get8(s); - int tga_image_type = stbi__get8(s); - int tga_is_RLE = 0; - int tga_palette_start = stbi__get16le(s); - int tga_palette_len = stbi__get16le(s); - int tga_palette_bits = stbi__get8(s); - int tga_x_origin = stbi__get16le(s); - int tga_y_origin = stbi__get16le(s); - int tga_width = stbi__get16le(s); - int tga_height = stbi__get16le(s); - int tga_bits_per_pixel = stbi__get8(s); - int tga_comp, tga_rgb16=0; - int tga_inverted = stbi__get8(s); - // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) - // image data - unsigned char *tga_data; - unsigned char *tga_palette = NULL; - int i, j; - unsigned char raw_data[4] = {0}; - int RLE_count = 0; - int RLE_repeating = 0; - int read_next_pixel = 1; - STBI_NOTUSED(ri); - - // do a tiny bit of precessing - if ( tga_image_type >= 8 ) - { - tga_image_type -= 8; - tga_is_RLE = 1; - } - tga_inverted = 1 - ((tga_inverted >> 5) & 1); - - // If I'm paletted, then I'll use the number of bits from the palette - if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); - else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); - - if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency - return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); - - // tga info - *x = tga_width; - *y = tga_height; - if (comp) *comp = tga_comp; - - if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) - return stbi__errpuc("too large", "Corrupt TGA"); - - tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); - if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); - - // skip to the data's starting position (offset usually = 0) - stbi__skip(s, tga_offset ); - - if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { - for (i=0; i < tga_height; ++i) { - int row = tga_inverted ? tga_height -i - 1 : i; - stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; - stbi__getn(s, tga_row, tga_width * tga_comp); - } - } else { - // do I need to load a palette? - if ( tga_indexed) - { - // any data to skip? (offset usually = 0) - stbi__skip(s, tga_palette_start ); - // load the palette - tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); - if (!tga_palette) { - STBI_FREE(tga_data); - return stbi__errpuc("outofmem", "Out of memory"); - } - if (tga_rgb16) { - stbi_uc *pal_entry = tga_palette; - STBI_ASSERT(tga_comp == STBI_rgb); - for (i=0; i < tga_palette_len; ++i) { - stbi__tga_read_rgb16(s, pal_entry); - pal_entry += tga_comp; - } - } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { - STBI_FREE(tga_data); - STBI_FREE(tga_palette); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - } - // load the data - for (i=0; i < tga_width * tga_height; ++i) - { - // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? - if ( tga_is_RLE ) - { - if ( RLE_count == 0 ) - { - // yep, get the next byte as a RLE command - int RLE_cmd = stbi__get8(s); - RLE_count = 1 + (RLE_cmd & 127); - RLE_repeating = RLE_cmd >> 7; - read_next_pixel = 1; - } else if ( !RLE_repeating ) - { - read_next_pixel = 1; - } - } else - { - read_next_pixel = 1; - } - // OK, if I need to read a pixel, do it now - if ( read_next_pixel ) - { - // load however much data we did have - if ( tga_indexed ) - { - // read in index, then perform the lookup - int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); - if ( pal_idx >= tga_palette_len ) { - // invalid index - pal_idx = 0; - } - pal_idx *= tga_comp; - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = tga_palette[pal_idx+j]; - } - } else if(tga_rgb16) { - STBI_ASSERT(tga_comp == STBI_rgb); - stbi__tga_read_rgb16(s, raw_data); - } else { - // read in the data raw - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = stbi__get8(s); - } - } - // clear the reading flag for the next pixel - read_next_pixel = 0; - } // end of reading a pixel - - // copy data - for (j = 0; j < tga_comp; ++j) - tga_data[i*tga_comp+j] = raw_data[j]; - - // in case we're in RLE mode, keep counting down - --RLE_count; - } - // do I need to invert the image? - if ( tga_inverted ) - { - for (j = 0; j*2 < tga_height; ++j) - { - int index1 = j * tga_width * tga_comp; - int index2 = (tga_height - 1 - j) * tga_width * tga_comp; - for (i = tga_width * tga_comp; i > 0; --i) - { - unsigned char temp = tga_data[index1]; - tga_data[index1] = tga_data[index2]; - tga_data[index2] = temp; - ++index1; - ++index2; - } - } - } - // clear my palette, if I had one - if ( tga_palette != NULL ) - { - STBI_FREE( tga_palette ); - } - } - - // swap RGB - if the source data was RGB16, it already is in the right order - if (tga_comp >= 3 && !tga_rgb16) - { - unsigned char* tga_pixel = tga_data; - for (i=0; i < tga_width * tga_height; ++i) - { - unsigned char temp = tga_pixel[0]; - tga_pixel[0] = tga_pixel[2]; - tga_pixel[2] = temp; - tga_pixel += tga_comp; - } - } - - // convert to target component count - if (req_comp && req_comp != tga_comp) - tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); - - // the things I do to get rid of an error message, and yet keep - // Microsoft's C compilers happy... [8^( - tga_palette_start = tga_palette_len = tga_palette_bits = - tga_x_origin = tga_y_origin = 0; - // OK, done - return tga_data; -} -#endif - -// ************************************************************************************************* -// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s) -{ - int r = (stbi__get32be(s) == 0x38425053); - stbi__rewind(s); - return r; -} - -static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) -{ - int count, nleft, len; - - count = 0; - while ((nleft = pixelCount - count) > 0) { - len = stbi__get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - if (len > nleft) return 0; // corrupt data - count += len; - while (len) { - *p = stbi__get8(s); - p += 4; - len--; - } - } else if (len > 128) { - stbi_uc val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len = 257 - len; - if (len > nleft) return 0; // corrupt data - val = stbi__get8(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } - } - - return 1; -} - -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - int pixelCount; - int channelCount, compression; - int channel, i; - int bitdepth; - int w,h; - stbi_uc *out; - STBI_NOTUSED(ri); - - // Check identifier - if (stbi__get32be(s) != 0x38425053) // "8BPS" - return stbi__errpuc("not PSD", "Corrupt PSD image"); - - // Check file type version. - if (stbi__get16be(s) != 1) - return stbi__errpuc("wrong version", "Unsupported version of PSD image"); - - // Skip 6 reserved bytes. - stbi__skip(s, 6 ); - - // Read the number of channels (R, G, B, A, etc). - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) - return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); - - // Read the rows and columns of the image. - h = stbi__get32be(s); - w = stbi__get32be(s); - - // Make sure the depth is 8 bits. - bitdepth = stbi__get16be(s); - if (bitdepth != 8 && bitdepth != 16) - return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); - - // Make sure the color mode is RGB. - // Valid options are: - // 0: Bitmap - // 1: Grayscale - // 2: Indexed color - // 3: RGB color - // 4: CMYK color - // 7: Multichannel - // 8: Duotone - // 9: Lab color - if (stbi__get16be(s) != 3) - return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); - - // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) - stbi__skip(s,stbi__get32be(s) ); - - // Skip the image resources. (resolution, pen tool paths, etc) - stbi__skip(s, stbi__get32be(s) ); - - // Skip the reserved data. - stbi__skip(s, stbi__get32be(s) ); - - // Find out if the data is compressed. - // Known values: - // 0: no compression - // 1: RLE compressed - compression = stbi__get16be(s); - if (compression > 1) - return stbi__errpuc("bad compression", "PSD has an unknown compression format"); - - // Check size - if (!stbi__mad3sizes_valid(4, w, h, 0)) - return stbi__errpuc("too large", "Corrupt PSD"); - - // Create the destination image. - - if (!compression && bitdepth == 16 && bpc == 16) { - out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); - ri->bits_per_channel = 16; - } else - out = (stbi_uc *) stbi__malloc(4 * w*h); - - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - pixelCount = w*h; - - // Initialize the data to zero. - //memset( out, 0, pixelCount * 4 ); - - // Finally, the image data. - if (compression) { - // RLE as used by .PSD and .TIFF - // Loop until you get the number of unpacked bytes you are expecting: - // Read the next source byte into n. - // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. - // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. - // Else if n is 128, noop. - // Endloop - - // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, - // which we're going to just skip. - stbi__skip(s, h * channelCount * 2 ); - - // Read the RLE data by channel. - for (channel = 0; channel < 4; channel++) { - stbi_uc *p; - - p = out+channel; - if (channel >= channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++, p += 4) - *p = (channel == 3 ? 255 : 0); - } else { - // Read the RLE data. - if (!stbi__psd_decode_rle(s, p, pixelCount)) { - STBI_FREE(out); - return stbi__errpuc("corrupt", "bad RLE data"); - } - } - } - - } else { - // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. - - // Read the data by channel. - for (channel = 0; channel < 4; channel++) { - if (channel >= channelCount) { - // Fill this channel with default data. - if (bitdepth == 16 && bpc == 16) { - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - stbi__uint16 val = channel == 3 ? 65535 : 0; - for (i = 0; i < pixelCount; i++, q += 4) - *q = val; - } else { - stbi_uc *p = out+channel; - stbi_uc val = channel == 3 ? 255 : 0; - for (i = 0; i < pixelCount; i++, p += 4) - *p = val; - } - } else { - if (ri->bits_per_channel == 16) { // output bpc - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - for (i = 0; i < pixelCount; i++, q += 4) - *q = (stbi__uint16) stbi__get16be(s); - } else { - stbi_uc *p = out+channel; - if (bitdepth == 16) { // input bpc - for (i = 0; i < pixelCount; i++, p += 4) - *p = (stbi_uc) (stbi__get16be(s) >> 8); - } else { - for (i = 0; i < pixelCount; i++, p += 4) - *p = stbi__get8(s); - } - } - } - } - } - - // remove weird white matte from PSD - if (channelCount >= 4) { - if (ri->bits_per_channel == 16) { - for (i=0; i < w*h; ++i) { - stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; - if (pixel[3] != 0 && pixel[3] != 65535) { - float a = pixel[3] / 65535.0f; - float ra = 1.0f / a; - float inv_a = 65535.0f * (1 - ra); - pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); - pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); - pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); - } - } - } else { - for (i=0; i < w*h; ++i) { - unsigned char *pixel = out + 4*i; - if (pixel[3] != 0 && pixel[3] != 255) { - float a = pixel[3] / 255.0f; - float ra = 1.0f / a; - float inv_a = 255.0f * (1 - ra); - pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); - pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); - pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); - } - } - } - } - - // convert to desired output format - if (req_comp && req_comp != 4) { - if (ri->bits_per_channel == 16) - out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); - else - out = stbi__convert_format(out, 4, req_comp, w, h); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - if (comp) *comp = 4; - *y = h; - *x = w; - - return out; -} -#endif - -// ************************************************************************************************* -// Softimage PIC loader -// by Tom Seddon -// -// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format -// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ - -#ifndef STBI_NO_PIC -static int stbi__pic_is4(stbi__context *s,const char *str) -{ - int i; - for (i=0; i<4; ++i) - if (stbi__get8(s) != (stbi_uc)str[i]) - return 0; - - return 1; -} - -static int stbi__pic_test_core(stbi__context *s) -{ - int i; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) - return 0; - - for(i=0;i<84;++i) - stbi__get8(s); - - if (!stbi__pic_is4(s,"PICT")) - return 0; - - return 1; -} - -typedef struct -{ - stbi_uc size,type,channel; -} stbi__pic_packet; - -static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) -{ - int mask=0x80, i; - - for (i=0; i<4; ++i, mask>>=1) { - if (channel & mask) { - if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); - dest[i]=stbi__get8(s); - } - } - - return dest; -} - -static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) -{ - int mask=0x80,i; - - for (i=0;i<4; ++i, mask>>=1) - if (channel&mask) - dest[i]=src[i]; -} - -static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) -{ - int act_comp=0,num_packets=0,y,chained; - stbi__pic_packet packets[10]; - - // this will (should...) cater for even some bizarre stuff like having data - // for the same channel in multiple packets. - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return stbi__errpuc("bad format","too many packets"); - - packet = &packets[num_packets++]; - - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - - act_comp |= packet->channel; - - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); - if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? - - for(y=0; ytype) { - default: - return stbi__errpuc("bad format","packet has bad compression type"); - - case 0: {//uncompressed - int x; - - for(x=0;xchannel,dest)) - return 0; - break; - } - - case 1://Pure RLE - { - int left=width, i; - - while (left>0) { - stbi_uc count,value[4]; - - count=stbi__get8(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); - - if (count > left) - count = (stbi_uc) left; - - if (!stbi__readval(s,packet->channel,value)) return 0; - - for(i=0; ichannel,dest,value); - left -= count; - } - } - break; - - case 2: {//Mixed RLE - int left=width; - while (left>0) { - int count = stbi__get8(s), i; - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); - - if (count >= 128) { // Repeated - stbi_uc value[4]; - - if (count==128) - count = stbi__get16be(s); - else - count -= 127; - if (count > left) - return stbi__errpuc("bad file","scanline overrun"); - - if (!stbi__readval(s,packet->channel,value)) - return 0; - - for(i=0;ichannel,dest,value); - } else { // Raw - ++count; - if (count>left) return stbi__errpuc("bad file","scanline overrun"); - - for(i=0;ichannel,dest)) - return 0; - } - left-=count; - } - break; - } - } - } - } - - return result; -} - -static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) -{ - stbi_uc *result; - int i, x,y, internal_comp; - STBI_NOTUSED(ri); - - if (!comp) comp = &internal_comp; - - for (i=0; i<92; ++i) - stbi__get8(s); - - x = stbi__get16be(s); - y = stbi__get16be(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); - if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); - - stbi__get32be(s); //skip `ratio' - stbi__get16be(s); //skip `fields' - stbi__get16be(s); //skip `pad' - - // intermediate buffer is RGBA - result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); - memset(result, 0xff, x*y*4); - - if (!stbi__pic_load_core(s,x,y,comp, result)) { - STBI_FREE(result); - result=0; - } - *px = x; - *py = y; - if (req_comp == 0) req_comp = *comp; - result=stbi__convert_format(result,4,req_comp,x,y); - - return result; -} - -static int stbi__pic_test(stbi__context *s) -{ - int r = stbi__pic_test_core(s); - stbi__rewind(s); - return r; -} -#endif - -// ************************************************************************************************* -// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb - -#ifndef STBI_NO_GIF -typedef struct -{ - stbi__int16 prefix; - stbi_uc first; - stbi_uc suffix; -} stbi__gif_lzw; - -typedef struct -{ - int w,h; - stbi_uc *out; // output buffer (always 4 components) - stbi_uc *background; // The current "background" as far as a gif is concerned - stbi_uc *history; - int flags, bgindex, ratio, transparent, eflags; - stbi_uc pal[256][4]; - stbi_uc lpal[256][4]; - stbi__gif_lzw codes[8192]; - stbi_uc *color_table; - int parse, step; - int lflags; - int start_x, start_y; - int max_x, max_y; - int cur_x, cur_y; - int line_size; - int delay; -} stbi__gif; - -static int stbi__gif_test_raw(stbi__context *s) -{ - int sz; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; - sz = stbi__get8(s); - if (sz != '9' && sz != '7') return 0; - if (stbi__get8(s) != 'a') return 0; - return 1; -} - -static int stbi__gif_test(stbi__context *s) -{ - int r = stbi__gif_test_raw(s); - stbi__rewind(s); - return r; -} - -static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) -{ - int i; - for (i=0; i < num_entries; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - pal[i][3] = transp == i ? 0 : 255; - } -} - -static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) -{ - stbi_uc version; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') - return stbi__err("not GIF", "Corrupt GIF"); - - version = stbi__get8(s); - if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); - if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); - - stbi__g_failure_reason = ""; - g->w = stbi__get16le(s); - g->h = stbi__get16le(s); - g->flags = stbi__get8(s); - g->bgindex = stbi__get8(s); - g->ratio = stbi__get8(s); - g->transparent = -1; - - if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments - - if (is_info) return 1; - - if (g->flags & 0x80) - stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); - - return 1; -} - -static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); - if (!stbi__gif_header(s, g, comp, 1)) { - STBI_FREE(g); - stbi__rewind( s ); - return 0; - } - if (x) *x = g->w; - if (y) *y = g->h; - STBI_FREE(g); - return 1; -} - -static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) -{ - stbi_uc *p, *c; - int idx; - - // recurse to decode the prefixes, since the linked-list is backwards, - // and working backwards through an interleaved image would be nasty - if (g->codes[code].prefix >= 0) - stbi__out_gif_code(g, g->codes[code].prefix); - - if (g->cur_y >= g->max_y) return; - - idx = g->cur_x + g->cur_y; - p = &g->out[idx]; - g->history[idx / 4] = 1; - - c = &g->color_table[g->codes[code].suffix * 4]; - if (c[3] > 128) { // don't render transparent pixels; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } - g->cur_x += 4; - - if (g->cur_x >= g->max_x) { - g->cur_x = g->start_x; - g->cur_y += g->step; - - while (g->cur_y >= g->max_y && g->parse > 0) { - g->step = (1 << g->parse) * g->line_size; - g->cur_y = g->start_y + (g->step >> 1); - --g->parse; - } - } -} - -static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) -{ - stbi_uc lzw_cs; - stbi__int32 len, init_code; - stbi__uint32 first; - stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; - stbi__gif_lzw *p; - - lzw_cs = stbi__get8(s); - if (lzw_cs > 12) return NULL; - clear = 1 << lzw_cs; - first = 1; - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - bits = 0; - valid_bits = 0; - for (init_code = 0; init_code < clear; init_code++) { - g->codes[init_code].prefix = -1; - g->codes[init_code].first = (stbi_uc) init_code; - g->codes[init_code].suffix = (stbi_uc) init_code; - } - - // support no starting clear code - avail = clear+2; - oldcode = -1; - - len = 0; - for(;;) { - if (valid_bits < codesize) { - if (len == 0) { - len = stbi__get8(s); // start new block - if (len == 0) - return g->out; - } - --len; - bits |= (stbi__int32) stbi__get8(s) << valid_bits; - valid_bits += 8; - } else { - stbi__int32 code = bits & codemask; - bits >>= codesize; - valid_bits -= codesize; - // @OPTIMIZE: is there some way we can accelerate the non-clear path? - if (code == clear) { // clear code - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - avail = clear + 2; - oldcode = -1; - first = 0; - } else if (code == clear + 1) { // end of stream code - stbi__skip(s, len); - while ((len = stbi__get8(s)) > 0) - stbi__skip(s,len); - return g->out; - } else if (code <= avail) { - if (first) { - return stbi__errpuc("no clear code", "Corrupt GIF"); - } - - if (oldcode >= 0) { - p = &g->codes[avail++]; - if (avail > 8192) { - return stbi__errpuc("too many codes", "Corrupt GIF"); - } - - p->prefix = (stbi__int16) oldcode; - p->first = g->codes[oldcode].first; - p->suffix = (code == avail) ? p->first : g->codes[code].first; - } else if (code == avail) - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - - stbi__out_gif_code(g, (stbi__uint16) code); - - if ((avail & codemask) == 0 && avail <= 0x0FFF) { - codesize++; - codemask = (1 << codesize) - 1; - } - - oldcode = code; - } else { - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - } - } - } -} - -// this function is designed to support animated gifs, although stb_image doesn't support it -// two back is the image from two frames ago, used for a very specific disposal format -static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) -{ - int dispose; - int first_frame; - int pi; - int pcount; - - // on first frame, any non-written pixels get the background colour (non-transparent) - first_frame = 0; - if (g->out == 0) { - if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header - g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - g->background = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - g->history = (stbi_uc *) stbi__malloc(g->w * g->h); - if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); - - // image is treated as "tranparent" at the start - ie, nothing overwrites the current background; - // background colour is only used for pixels that are not rendered first frame, after that "background" - // color refers to teh color that was there the previous frame. - memset( g->out, 0x00, 4 * g->w * g->h ); - memset( g->background, 0x00, 4 * g->w * g->h ); // state of the background (starts transparent) - memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame - first_frame = 1; - } else { - // second frame - how do we dispoase of the previous one? - dispose = (g->eflags & 0x1C) >> 2; - pcount = g->w * g->h; - - if ((dispose == 3) && (two_back == 0)) { - dispose = 2; // if I don't have an image to revert back to, default to the old background - } - - if (dispose == 3) { // use previous graphic - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); - } - } - } else if (dispose == 2) { - // restore what was changed last frame to background before that frame; - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); - } - } - } else { - // This is a non-disposal case eithe way, so just - // leave the pixels as is, and they will become the new background - // 1: do not dispose - // 0: not specified. - } - - // background is what out is after the undoing of the previou frame; - memcpy( g->background, g->out, 4 * g->w * g->h ); - } - - // clear my history; - memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame - - for (;;) { - int tag = stbi__get8(s); - switch (tag) { - case 0x2C: /* Image Descriptor */ - { - stbi__int32 x, y, w, h; - stbi_uc *o; - - x = stbi__get16le(s); - y = stbi__get16le(s); - w = stbi__get16le(s); - h = stbi__get16le(s); - if (((x + w) > (g->w)) || ((y + h) > (g->h))) - return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); - - g->line_size = g->w * 4; - g->start_x = x * 4; - g->start_y = y * g->line_size; - g->max_x = g->start_x + w * 4; - g->max_y = g->start_y + h * g->line_size; - g->cur_x = g->start_x; - g->cur_y = g->start_y; - - g->lflags = stbi__get8(s); - - if (g->lflags & 0x40) { - g->step = 8 * g->line_size; // first interlaced spacing - g->parse = 3; - } else { - g->step = g->line_size; - g->parse = 0; - } - - if (g->lflags & 0x80) { - stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); - g->color_table = (stbi_uc *) g->lpal; - } else if (g->flags & 0x80) { - g->color_table = (stbi_uc *) g->pal; - } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - - o = stbi__process_gif_raster(s, g); - if (o == NULL) return NULL; - - // if this was the first frame, - pcount = g->w * g->h; - if (first_frame && (g->bgindex > 0)) { - // if first frame, any pixel not drawn to gets the background color - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi] == 0) { - g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; - memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); - } - } - } - - return o; - } - - case 0x21: // Comment Extension. - { - int len; - int ext = stbi__get8(s); - if (ext == 0xF9) { // Graphic Control Extension. - len = stbi__get8(s); - if (len == 4) { - g->eflags = stbi__get8(s); - g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. - - // unset old transparent - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 255; - } - if (g->eflags & 0x01) { - g->transparent = stbi__get8(s); - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 0; - } - } else { - // don't need transparent - stbi__skip(s, 1); - g->transparent = -1; - } - } else { - stbi__skip(s, len); - break; - } - } - while ((len = stbi__get8(s)) != 0) { - stbi__skip(s, len); - } - break; - } - - case 0x3B: // gif stream termination code - return (stbi_uc *) s; // using '1' causes warning on some compilers - - default: - return stbi__errpuc("unknown code", "Corrupt GIF"); - } - } -} - -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - if (stbi__gif_test(s)) { - int layers = 0; - stbi_uc *u = 0; - stbi_uc *out = 0; - stbi_uc *two_back = 0; - stbi__gif g; - int stride; - memset(&g, 0, sizeof(g)); - if (delays) { - *delays = 0; - } - - do { - u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - - if (u) { - *x = g.w; - *y = g.h; - ++layers; - stride = g.w * g.h * 4; - - if (out) { - out = (stbi_uc*) STBI_REALLOC( out, layers * stride ); - if (delays) { - *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); - } - } else { - out = (stbi_uc*)stbi__malloc( layers * stride ); - if (delays) { - *delays = (int*) stbi__malloc( layers * sizeof(int) ); - } - } - memcpy( out + ((layers - 1) * stride), u, stride ); - if (layers >= 2) { - two_back = out - 2 * stride; - } - - if (delays) { - (*delays)[layers - 1U] = g.delay; - } - } - } while (u != 0); - - // free temp buffer; - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); - - // do the final conversion after loading everything; - if (req_comp && req_comp != 4) - out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); - - *z = layers; - return out; - } else { - return stbi__errpuc("not GIF", "Image was not as a gif type."); - } -} - -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *u = 0; - stbi__gif g; - memset(&g, 0, sizeof(g)); - - u = stbi__gif_load_next(s, &g, comp, req_comp, 0); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - if (u) { - *x = g.w; - *y = g.h; - - // moved conversion to after successful load so that the same - // can be done for multiple frames. - if (req_comp && req_comp != 4) - u = stbi__convert_format(u, 4, req_comp, g.w, g.h); - } - - // free buffers needed for multiple frame loading; - STBI_FREE(g.history); - STBI_FREE(g.background); - - return u; -} - -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) -{ - return stbi__gif_info_raw(s,x,y,comp); -} -#endif - -// ************************************************************************************************* -// Radiance RGBE HDR loader -// originally by Nicolas Schulz -#ifndef STBI_NO_HDR -static int stbi__hdr_test_core(stbi__context *s, const char *signature) -{ - int i; - for (i=0; signature[i]; ++i) - if (stbi__get8(s) != signature[i]) - return 0; - stbi__rewind(s); - return 1; -} - -static int stbi__hdr_test(stbi__context* s) -{ - int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); - stbi__rewind(s); - if(!r) { - r = stbi__hdr_test_core(s, "#?RGBE\n"); - stbi__rewind(s); - } - return r; -} - -#define STBI__HDR_BUFLEN 1024 -static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) -{ - int len=0; - char c = '\0'; - - c = (char) stbi__get8(z); - - while (!stbi__at_eof(z) && c != '\n') { - buffer[len++] = c; - if (len == STBI__HDR_BUFLEN-1) { - // flush to end of line - while (!stbi__at_eof(z) && stbi__get8(z) != '\n') - ; - break; - } - c = (char) stbi__get8(z); - } - - buffer[len] = 0; - return buffer; -} - -static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) -{ - if ( input[3] != 0 ) { - float f1; - // Exponent - f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); - if (req_comp <= 2) - output[0] = (input[0] + input[1] + input[2]) * f1 / 3; - else { - output[0] = input[0] * f1; - output[1] = input[1] * f1; - output[2] = input[2] * f1; - } - if (req_comp == 2) output[1] = 1; - if (req_comp == 4) output[3] = 1; - } else { - switch (req_comp) { - case 4: output[3] = 1; /* fallthrough */ - case 3: output[0] = output[1] = output[2] = 0; - break; - case 2: output[1] = 1; /* fallthrough */ - case 1: output[0] = 0; - break; - } - } -} - -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int width, height; - stbi_uc *scanline; - float *hdr_data; - int len; - unsigned char count, value; - int i, j, k, c1,c2, z; - const char *headerToken; - STBI_NOTUSED(ri); - - // Check identifier - headerToken = stbi__hdr_gettoken(s,buffer); - if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) - return stbi__errpf("not HDR", "Corrupt HDR image"); - - // Parse header - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); - - // Parse width and height - // can't use sscanf() if we're not using stdio! - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - height = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - width = (int) strtol(token, NULL, 10); - - *x = width; - *y = height; - - if (comp) *comp = 3; - if (req_comp == 0) req_comp = 3; - - if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) - return stbi__errpf("too large", "HDR image is too large"); - - // Read data - hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); - if (!hdr_data) - return stbi__errpf("outofmem", "Out of memory"); - - // Load image data - // image data is stored as some number of sca - if ( width < 8 || width >= 32768) { - // Read flat data - for (j=0; j < height; ++j) { - for (i=0; i < width; ++i) { - stbi_uc rgbe[4]; - main_decode_loop: - stbi__getn(s, rgbe, 4); - stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); - } - } - } else { - // Read RLE-encoded data - scanline = NULL; - - for (j = 0; j < height; ++j) { - c1 = stbi__get8(s); - c2 = stbi__get8(s); - len = stbi__get8(s); - if (c1 != 2 || c2 != 2 || (len & 0x80)) { - // not run-length encoded, so we have to actually use THIS data as a decoded - // pixel (note this can't be a valid pixel--one of RGB must be >= 128) - stbi_uc rgbe[4]; - rgbe[0] = (stbi_uc) c1; - rgbe[1] = (stbi_uc) c2; - rgbe[2] = (stbi_uc) len; - rgbe[3] = (stbi_uc) stbi__get8(s); - stbi__hdr_convert(hdr_data, rgbe, req_comp); - i = 1; - j = 0; - STBI_FREE(scanline); - goto main_decode_loop; // yes, this makes no sense - } - len <<= 8; - len |= stbi__get8(s); - if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) { - scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); - if (!scanline) { - STBI_FREE(hdr_data); - return stbi__errpf("outofmem", "Out of memory"); - } - } - - for (k = 0; k < 4; ++k) { - int nleft; - i = 0; - while ((nleft = width - i) > 0) { - count = stbi__get8(s); - if (count > 128) { - // Run - value = stbi__get8(s); - count -= 128; - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = value; - } else { - // Dump - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = stbi__get8(s); - } - } - } - for (i=0; i < width; ++i) - stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); - } - if (scanline) - STBI_FREE(scanline); - } - - return hdr_data; -} - -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int dummy; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - if (stbi__hdr_test(s) == 0) { - stbi__rewind( s ); - return 0; - } - - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) { - stbi__rewind( s ); - return 0; - } - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *y = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *x = (int) strtol(token, NULL, 10); - *comp = 3; - return 1; -} -#endif // STBI_NO_HDR - -#ifndef STBI_NO_BMP -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) -{ - void *p; - stbi__bmp_data info; - - info.all_a = 255; - p = stbi__bmp_parse_header(s, &info); - stbi__rewind( s ); - if (p == NULL) - return 0; - if (x) *x = s->img_x; - if (y) *y = s->img_y; - if (comp) *comp = info.ma ? 4 : 3; - return 1; -} -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) -{ - int channelCount, dummy, depth; - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - *y = stbi__get32be(s); - *x = stbi__get32be(s); - depth = stbi__get16be(s); - if (depth != 8 && depth != 16) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 3) { - stbi__rewind( s ); - return 0; - } - *comp = 4; - return 1; -} - -static int stbi__psd_is16(stbi__context *s) -{ - int channelCount, depth; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - (void) stbi__get32be(s); - (void) stbi__get32be(s); - depth = stbi__get16be(s); - if (depth != 16) { - stbi__rewind( s ); - return 0; - } - return 1; -} -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) -{ - int act_comp=0,num_packets=0,chained,dummy; - stbi__pic_packet packets[10]; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { - stbi__rewind(s); - return 0; - } - - stbi__skip(s, 88); - - *x = stbi__get16be(s); - *y = stbi__get16be(s); - if (stbi__at_eof(s)) { - stbi__rewind( s); - return 0; - } - if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { - stbi__rewind( s ); - return 0; - } - - stbi__skip(s, 8); - - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return 0; - - packet = &packets[num_packets++]; - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - act_comp |= packet->channel; - - if (stbi__at_eof(s)) { - stbi__rewind( s ); - return 0; - } - if (packet->size != 8) { - stbi__rewind( s ); - return 0; - } - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); - - return 1; -} -#endif - -// ************************************************************************************************* -// Portable Gray Map and Portable Pixel Map loader -// by Ken Miller -// -// PGM: http://netpbm.sourceforge.net/doc/pgm.html -// PPM: http://netpbm.sourceforge.net/doc/ppm.html -// -// Known limitations: -// Does not support comments in the header section -// Does not support ASCII image data (formats P2 and P3) -// Does not support 16-bit-per-channel - -#ifndef STBI_NO_PNM - -static int stbi__pnm_test(stbi__context *s) -{ - char p, t; - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind( s ); - return 0; - } - return 1; -} - -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - STBI_NOTUSED(ri); - - if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) - return 0; - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - - if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "PNM too large"); - - out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - stbi__getn(s, out, s->img_n * s->img_x * s->img_y); - - if (req_comp && req_comp != s->img_n) { - out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - return out; -} - -static int stbi__pnm_isspace(char c) -{ - return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; -} - -static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) -{ - for (;;) { - while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) - *c = (char) stbi__get8(s); - - if (stbi__at_eof(s) || *c != '#') - break; - - while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) - *c = (char) stbi__get8(s); - } -} - -static int stbi__pnm_isdigit(char c) -{ - return c >= '0' && c <= '9'; -} - -static int stbi__pnm_getinteger(stbi__context *s, char *c) -{ - int value = 0; - - while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { - value = value*10 + (*c - '0'); - *c = (char) stbi__get8(s); - } - - return value; -} - -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) -{ - int maxv, dummy; - char c, p, t; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - stbi__rewind(s); - - // Get identifier - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind(s); - return 0; - } - - *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm - - c = (char) stbi__get8(s); - stbi__pnm_skip_whitespace(s, &c); - - *x = stbi__pnm_getinteger(s, &c); // read width - stbi__pnm_skip_whitespace(s, &c); - - *y = stbi__pnm_getinteger(s, &c); // read height - stbi__pnm_skip_whitespace(s, &c); - - maxv = stbi__pnm_getinteger(s, &c); // read max value - - if (maxv > 255) - return stbi__err("max value > 255", "PPM image not 8-bit"); - else - return 1; -} -#endif - -static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) -{ - #ifndef STBI_NO_JPEG - if (stbi__jpeg_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNG - if (stbi__png_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_GIF - if (stbi__gif_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_BMP - if (stbi__bmp_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PIC - if (stbi__pic_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNM - if (stbi__pnm_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_info(s, x, y, comp)) return 1; - #endif - - // test tga last because it's a crappy test! - #ifndef STBI_NO_TGA - if (stbi__tga_info(s, x, y, comp)) - return 1; - #endif - return stbi__err("unknown image type", "Image not of any known type, or corrupt"); -} - -static int stbi__is_16_main(stbi__context *s) -{ - #ifndef STBI_NO_PNG - if (stbi__png_is16(s)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_is16(s)) return 1; - #endif - - return 0; -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_info_from_file(f, x, y, comp); - fclose(f); - return result; -} - -STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__info_main(&s,x,y,comp); - fseek(f,pos,SEEK_SET); - return r; -} - -STBIDEF int stbi_is_16_bit(char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_is_16_bit_from_file(f); - fclose(f); - return result; -} - -STBIDEF int stbi_is_16_bit_from_file(FILE *f) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__is_16_main(&s); - fseek(f,pos,SEEK_SET); - return r; -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__is_16_main(&s); -} - -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__is_16_main(&s); -} - -#endif // STB_IMAGE_IMPLEMENTATION - -/* - revision history: - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug - 1-bit BMP - *_is_16_bit api - avoid warnings - 2.16 (2017-07-23) all functions have 16-bit variants; - STBI_NO_STDIO works again; - compilation fixes; - fix rounding in unpremultiply; - optimize vertical flip; - disable raw_len validation; - documentation fixes - 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; - warning fixes; disable run-time SSE detection on gcc; - uniform handling of optional "return" values; - thread-safe initialization of zlib tables - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) allocate large structures on the stack - remove white matting for transparent PSD - fix reported channel count for PNG & BMP - re-enable SSE2 in non-gcc 64-bit - support RGB-formatted JPEG - read 16-bit PNGs (only as 8-bit) - 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED - 2.09 (2016-01-16) allow comments in PNM files - 16-bit-per-pixel TGA (not bit-per-component) - info() for TGA could break due to .hdr handling - info() for BMP to shares code instead of sloppy parse - can use STBI_REALLOC_SIZED if allocator doesn't support realloc - code cleanup - 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA - 2.07 (2015-09-13) fix compiler warnings - partial animated GIF support - limited 16-bpc PSD support - #ifdef unused functions - bug with < 92 byte PIC,PNM,HDR,TGA - 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value - 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning - 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit - 2.03 (2015-04-12) extra corruption checking (mmozeiko) - stbi_set_flip_vertically_on_load (nguillemot) - fix NEON support; fix mingw support - 2.02 (2015-01-19) fix incorrect assert, fix warning - 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 - 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG - 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) - progressive JPEG (stb) - PGM/PPM support (Ken Miller) - STBI_MALLOC,STBI_REALLOC,STBI_FREE - GIF bugfix -- seemingly never worked - STBI_NO_*, STBI_ONLY_* - 1.48 (2014-12-14) fix incorrectly-named assert() - 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) - optimize PNG (ryg) - fix bug in interlaced PNG with user-specified channel count (stb) - 1.46 (2014-08-26) - fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG - 1.45 (2014-08-16) - fix MSVC-ARM internal compiler error by wrapping malloc - 1.44 (2014-08-07) - various warning fixes from Ronny Chevalier - 1.43 (2014-07-15) - fix MSVC-only compiler problem in code changed in 1.42 - 1.42 (2014-07-09) - don't define _CRT_SECURE_NO_WARNINGS (affects user code) - fixes to stbi__cleanup_jpeg path - added STBI_ASSERT to avoid requiring assert.h - 1.41 (2014-06-25) - fix search&replace from 1.36 that messed up comments/error messages - 1.40 (2014-06-22) - fix gcc struct-initialization warning - 1.39 (2014-06-15) - fix to TGA optimization when req_comp != number of components in TGA; - fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) - add support for BMP version 5 (more ignored fields) - 1.38 (2014-06-06) - suppress MSVC warnings on integer casts truncating values - fix accidental rename of 'skip' field of I/O - 1.37 (2014-06-04) - remove duplicate typedef - 1.36 (2014-06-03) - convert to header file single-file library - if de-iphone isn't set, load iphone images color-swapped instead of returning NULL - 1.35 (2014-05-27) - various warnings - fix broken STBI_SIMD path - fix bug where stbi_load_from_file no longer left file pointer in correct place - fix broken non-easy path for 32-bit BMP (possibly never used) - TGA optimization by Arseny Kapoulkine - 1.34 (unknown) - use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case - 1.33 (2011-07-14) - make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements - 1.32 (2011-07-13) - support for "info" function for all supported filetypes (SpartanJ) - 1.31 (2011-06-20) - a few more leak fixes, bug in PNG handling (SpartanJ) - 1.30 (2011-06-11) - added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) - removed deprecated format-specific test/load functions - removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway - error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) - fix inefficiency in decoding 32-bit BMP (David Woo) - 1.29 (2010-08-16) - various warning fixes from Aurelien Pocheville - 1.28 (2010-08-01) - fix bug in GIF palette transparency (SpartanJ) - 1.27 (2010-08-01) - cast-to-stbi_uc to fix warnings - 1.26 (2010-07-24) - fix bug in file buffering for PNG reported by SpartanJ - 1.25 (2010-07-17) - refix trans_data warning (Won Chun) - 1.24 (2010-07-12) - perf improvements reading from files on platforms with lock-heavy fgetc() - minor perf improvements for jpeg - deprecated type-specific functions so we'll get feedback if they're needed - attempt to fix trans_data warning (Won Chun) - 1.23 fixed bug in iPhone support - 1.22 (2010-07-10) - removed image *writing* support - stbi_info support from Jetro Lauha - GIF support from Jean-Marc Lienher - iPhone PNG-extensions from James Brown - warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) - 1.21 fix use of 'stbi_uc' in header (reported by jon blow) - 1.20 added support for Softimage PIC, by Tom Seddon - 1.19 bug in interlaced PNG corruption check (found by ryg) - 1.18 (2008-08-02) - fix a threading bug (local mutable static) - 1.17 support interlaced PNG - 1.16 major bugfix - stbi__convert_format converted one too many pixels - 1.15 initialize some fields for thread safety - 1.14 fix threadsafe conversion bug - header-file-only version (#define STBI_HEADER_FILE_ONLY before including) - 1.13 threadsafe - 1.12 const qualifiers in the API - 1.11 Support installable IDCT, colorspace conversion routines - 1.10 Fixes for 64-bit (don't use "unsigned long") - optimized upsampling by Fabian "ryg" Giesen - 1.09 Fix format-conversion for PSD code (bad global variables!) - 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz - 1.07 attempt to fix C++ warning/errors again - 1.06 attempt to fix C++ warning/errors again - 1.05 fix TGA loading to return correct *comp and use good luminance calc - 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free - 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR - 1.02 support for (subset of) HDR files, float interface for preferred access to them - 1.01 fix bug: possible bug in handling right-side up bmps... not sure - fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all - 1.00 interface to zlib that skips zlib header - 0.99 correct handling of alpha in palette - 0.98 TGA loader by lonesock; dynamically add loaders (untested) - 0.97 jpeg errors on too large a file; also catch another malloc failure - 0.96 fix detection of invalid v value - particleman@mollyrocket forum - 0.95 during header scan, seek to markers in case of padding - 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same - 0.93 handle jpegtran output; verbose errors - 0.92 read 4,8,16,24,32-bit BMP files of several formats - 0.91 output 24-bit Windows 3.0 BMP files - 0.90 fix a few more warnings; bump version number to approach 1.0 - 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd - 0.60 fix compiling as c++ - 0.59 fix warnings: merge Dave Moore's -Wall fixes - 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian - 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available - 0.56 fix bug: zlib uncompressed mode len vs. nlen - 0.55 fix bug: restart_interval not initialized to 0 - 0.54 allow NULL for 'int *comp' - 0.53 fix bug in png 3->4; speedup png decoding - 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments - 0.51 obey req_comp requests, 1-component jpegs return as 1-component, - on 'test' only check type, not whether we support this variant - 0.50 (2006-11-19) - first released version -*/ - - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- -*/