Add QR code installation over HTTP, other misc fixes and cleanups.

This commit is contained in:
Steven Smith 2016-04-21 21:21:22 -07:00
parent 2aed62c51f
commit 0ef50e1f40
34 changed files with 3860 additions and 448 deletions

@ -1 +1 @@
Subproject commit 8ed050d92a96c81bef4deab42e85c4351c05d1ef
Subproject commit 8d891f1ab9f37df9d9edad1d215d090f31ebbb11

View File

Before

Width:  |  Height:  |  Size: 339 B

After

Width:  |  Height:  |  Size: 339 B

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -26,6 +26,7 @@ void cleanup() {
}
amExit();
httpcExit();
ptmuExit();
acExit();
cfguExit();
@ -57,6 +58,7 @@ int main(int argc, const char* argv[]) {
cfguInit();
acInit();
ptmuInit();
httpcInit(0);
amInit();
AM_InitializeExternalTitleDatabase(false);
@ -71,10 +73,7 @@ int main(int argc, const char* argv[]) {
mainmenu_open();
while(aptMainLoop() && ui_top() != NULL) {
ui_update();
ui_draw();
}
while(aptMainLoop() && ui_update());
cleanup();
return 0;

16
source/quirc/LICENSE Normal file
View File

@ -0,0 +1,16 @@
quirc -- QR-code recognition library
Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
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.

884
source/quirc/decode.c Normal file
View File

@ -0,0 +1,884 @@
/* quirc -- QR-code recognition library
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
*
* 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 <string.h>
#include <stdlib.h>
#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_mult(uint8_t *r, const uint8_t *a, const uint8_t *b,
const struct galois_field *gf)
{
int i;
memset(r, 0, MAX_POLY);
for (i = 0; i < MAX_POLY; i++) {
int j;
for (j = 0; j + i < MAX_POLY; j++) {
uint8_t ca = a[i];
uint8_t cb = b[j];
if (!(ca && cb))
continue;
r[i + j] ^= gf->exp[(gf->log[ca] +
gf->log[cb]) %
gf->p];
}
}
}
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 + 1) * j) % 255];
}
if (s[i])
nonzero = 1;
}
return nonzero;
}
static quirc_decode_error_t correct_block(uint8_t *data, const struct quirc_rs_params *ecc)
{
int npar = ecc->ce;
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 */
poly_mult(omega, sigma, s, &gf256);
memset(omega + npar, 0, MAX_POLY - npar);
/* 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_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;
default:
goto done;
}
if (err)
return err;
if (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;
}

1186
source/quirc/identify.c Normal file

File diff suppressed because it is too large Load Diff

81
source/quirc/quirc.c Normal file
View File

@ -0,0 +1,81 @@
/* quirc -- QR-code recognition library
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
*
* 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 <stdlib.h>
#include <string.h>
#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);
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;
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";
}

145
source/quirc/quirc.h Normal file
View File

@ -0,0 +1,145 @@
/* quirc -- QR-code recognition library
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
*
* 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 <stdint.h>
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
/* 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;
};
/* 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

View File

@ -0,0 +1,103 @@
/* quirc -- QR-code recognition library
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
*
* 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
#define QUIRC_MAX_REGIONS 254
#define QUIRC_MAX_CAPSTONES 32
#define QUIRC_MAX_GRIDS 8
#define QUIRC_PERSPECTIVE_PARAMS 8
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;
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

421
source/quirc/version_db.c Normal file
View File

@ -0,0 +1,421 @@
/* quirc -- QR-code recognition library
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
*
* 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}
}
}
};

View File

@ -202,8 +202,8 @@ void screen_init() {
screen_load_texture_file(TEXTURE_BUTTON_LARGE, "button_large.png", true);
screen_load_texture_file(TEXTURE_PROGRESS_BAR_BG, "progress_bar_bg.png", true);
screen_load_texture_file(TEXTURE_PROGRESS_BAR_CONTENT, "progress_bar_content.png", true);
screen_load_texture_file(TEXTURE_SMDH_INFO_BOX, "smdh_info_box.png", true);
screen_load_texture_file(TEXTURE_SMDH_INFO_BOX_SHADOW, "smdh_info_box_shadow.png", true);
screen_load_texture_file(TEXTURE_META_INFO_BOX, "meta_info_box.png", true);
screen_load_texture_file(TEXTURE_META_INFO_BOX_SHADOW, "meta_info_box_shadow.png", true);
screen_load_texture_file(TEXTURE_BATTERY_CHARGING, "battery_charging.png", true);
screen_load_texture_file(TEXTURE_BATTERY_0, "battery0.png", true);
screen_load_texture_file(TEXTURE_BATTERY_1, "battery1.png", true);
@ -274,7 +274,7 @@ void screen_load_texture(u32 id, void* data, u32 size, u32 width, u32 height, GP
u8* pow2Tex = linearAlloc(pow2Width * pow2Height * pixelSize);
if(pow2Tex == NULL) {
util_panic("Failed to allocate temporary texture buffer for tiled data.");
util_panic("Failed to allocate temporary texture buffer.");
return;
}
@ -298,7 +298,7 @@ void screen_load_texture(u32 id, void* data, u32 size, u32 width, u32 height, GP
textures[id].pow2Height = pow2Height;
if(!C3D_TexInit(&textures[id].tex, (int) pow2Width, (int) pow2Height, format)) {
util_panic("Failed to initialize texture for tiled data.");
util_panic("Failed to initialize texture.");
return;
}
@ -306,16 +306,11 @@ void screen_load_texture(u32 id, void* data, u32 size, u32 width, u32 height, GP
Result flushRes = GSPGPU_FlushDataCache(pow2Tex, pow2Width * pow2Height * 4);
if(R_FAILED(flushRes)) {
util_panic("Failed to flush texture buffer for tiled data: 0x%08lX", flushRes);
return;
}
Result transferRes = GX_DisplayTransfer((u32*) pow2Tex, GX_BUFFER_DIM(pow2Width, pow2Height), (u32*) textures[id].tex.data, GX_BUFFER_DIM(pow2Width, pow2Height), GX_TRANSFER_FLIP_VERT(1) | GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_RAW_COPY(0) | GX_TRANSFER_IN_FORMAT((u32) gpuToGxFormat[format]) | GX_TRANSFER_OUT_FORMAT((u32) gpuToGxFormat[format]) | GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO));
if(R_FAILED(transferRes)) {
util_panic("Failed to tile texture data for tiled data: 0x%08lX", transferRes);
util_panic("Failed to flush texture buffer: 0x%08lX", flushRes);
return;
}
C3D_SafeDisplayTransfer((u32*) pow2Tex, GX_BUFFER_DIM(pow2Width, pow2Height), (u32*) textures[id].tex.data, GX_BUFFER_DIM(pow2Width, pow2Height), GX_TRANSFER_FLIP_VERT(1) | GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_RAW_COPY(0) | GX_TRANSFER_IN_FORMAT((u32) gpuToGxFormat[format]) | GX_TRANSFER_OUT_FORMAT((u32) gpuToGxFormat[format]) | GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO));
gspWaitForPPF();
linearFree(pow2Tex);
@ -347,7 +342,7 @@ void screen_load_texture_file(u32 id, const char* path, bool linearFilter) {
FILE* fd = util_open_resource(path);
if(fd == NULL) {
util_panic("Failed to load PNG file \"%s\": %s", strerror(errno));
util_panic("Failed to load PNG file \"%s\": %s", path, strerror(errno));
return;
}

View File

@ -24,8 +24,8 @@
#define TEXTURE_BUTTON_LARGE 13
#define TEXTURE_PROGRESS_BAR_BG 14
#define TEXTURE_PROGRESS_BAR_CONTENT 15
#define TEXTURE_SMDH_INFO_BOX 16
#define TEXTURE_SMDH_INFO_BOX_SHADOW 17
#define TEXTURE_META_INFO_BOX 16
#define TEXTURE_META_INFO_BOX_SHADOW 17
#define TEXTURE_BATTERY_CHARGING 18
#define TEXTURE_BATTERY_0 19
#define TEXTURE_BATTERY_1 20

View File

@ -1,5 +1,7 @@
#include <3ds.h>
#include <malloc.h>
#include <stdio.h>
#include <3ds.h>
#include "info.h"
#include "../screen.h"
@ -69,6 +71,7 @@ void info_display(const char* name, const char* info, bool bar, void* data, void
infoData->bar = bar;
infoData->data = data;
infoData->progress = 0;
snprintf(infoData->text, PROGRESS_TEXT_MAX, "Please wait...");
infoData->update = update;
infoData->drawTop = drawTop;

View File

@ -8,7 +8,7 @@
#include "section/section.h"
#include "../screen.h"
#define MAINMENU_ITEM_COUNT 12
#define MAINMENU_ITEM_COUNT 13
static u32 mainmenu_item_count = MAINMENU_ITEM_COUNT;
static list_item mainmenu_items[MAINMENU_ITEM_COUNT] = {
@ -24,6 +24,7 @@ static list_item mainmenu_items[MAINMENU_ITEM_COUNT] = {
{"Ext Save Data", COLOR_TEXT, extsavedata_open},
{"System Save Data", COLOR_TEXT, systemsavedata_open},
{"Network Install", COLOR_TEXT, networkinstall_open},
{"QR Code Install", COLOR_TEXT, qrinstall_open},
};
static void mainmenu_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) {

View File

@ -96,6 +96,7 @@ static void extsavedata_update(ui_view* view, void* data, list_item** items, u32
ui_pop();
list_destroy(view);
task_clear_ext_save_data(listData->items, &listData->count);
free(listData);
return;
}

View File

@ -260,6 +260,7 @@ static void files_update(ui_view* view, void* data, list_item** items, u32** ite
ui_pop();
list_destroy(view);
task_clear_files(listData->items, &listData->count);
free(listData);
return;
} else {

View File

@ -7,12 +7,12 @@
#include <3ds.h>
#include "action/action.h"
#include "task/task.h"
#include "section.h"
#include "../error.h"
#include "../info.h"
#include "../prompt.h"
#include "../../screen.h"
#include "section.h"
typedef struct {
int serverSocket;

View File

@ -95,6 +95,7 @@ static void pendingtitles_update(ui_view* view, void* data, list_item** items, u
ui_pop();
list_destroy(view);
task_clear_pending_titles(listData->items, &listData->count);
free(listData);
return;
}

View File

@ -0,0 +1,385 @@
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <3ds.h>
#include "task/task.h"
#include "../error.h"
#include "../info.h"
#include "../prompt.h"
#include "../../screen.h"
#include "../../quirc/quirc_internal.h"
#define IMAGE_WIDTH 400
#define IMAGE_HEIGHT 240
#define URL_MAX 1024
#define URLS_MAX 128
typedef struct {
struct quirc* qrContext;
char urls[URLS_MAX][URL_MAX];
Handle mutex;
u16* buffer;
u32 tex;
Handle camCancelEvent;
u32 responseCode;
u64 currTitleId;
data_op_info installInfo;
Handle installCancelEvent;
} qr_install_data;
static Result qrinstall_is_src_directory(void* data, u32 index, bool* isDirectory) {
*isDirectory = false;
return 0;
}
static Result qrinstall_make_dst_directory(void* data, u32 index) {
return 0;
}
static Result qrinstall_open_src(void* data, u32 index, u32* handle) {
qr_install_data* qrInstallData = (qr_install_data*) data;
httpcContext* context = (httpcContext*) calloc(1, sizeof(httpcContext));
Result res = 0;
if(R_SUCCEEDED(res = httpcOpenContext(context, HTTPC_METHOD_GET, qrInstallData->urls[index], 1))) {
httpcSetSSLOpt(context, SSLCOPT_DisableVerify);
if(R_SUCCEEDED(res = httpcBeginRequest(context)) && R_SUCCEEDED(res = httpcGetResponseStatusCode(context, &qrInstallData->responseCode, 0))) {
if(qrInstallData->responseCode == 200) {
*handle = (u32) context;
} else {
res = R_FBI_HTTP_RESPONSE_CODE;
}
}
if(R_FAILED(res)) {
httpcCloseContext(context);
}
}
if(R_FAILED(res)) {
free(context);
}
return res;
}
static Result qrinstall_close_src(void* data, u32 index, bool succeeded, u32 handle) {
return httpcCloseContext((httpcContext*) handle);
}
static Result qrinstall_get_src_size(void* data, u32 handle, u64* size) {
u32 downloadSize = 0;
Result res = httpcGetDownloadSizeState((httpcContext*) handle, NULL, &downloadSize);
*size = downloadSize;
return res;
}
static Result qrinstall_read_src(void* data, u32 handle, u32* bytesRead, void* buffer, u64 offset, u32 size) {
Result res = httpcDownloadData((httpcContext*) handle, buffer, size, bytesRead);
return res != HTTPC_RESULTCODE_DOWNLOADPENDING ? res : 0;
}
static Result qrinstall_open_dst(void* data, u32 index, void* initialReadBlock, u32* handle) {
qr_install_data* qrInstallData = (qr_install_data*) data;
u8* buffer = (u8*) initialReadBlock;
u32 headerSize = *(u32*) &buffer[0x00];
u32 certSize = *(u32*) &buffer[0x08];
u64 titleId = __builtin_bswap64(*(u64*) &buffer[((headerSize + 0x3F) & ~0x3F) + ((certSize + 0x3F) & ~0x3F) + 0x1DC]);
FS_MediaType dest = ((titleId >> 32) & 0x8010) != 0 ? MEDIATYPE_NAND : MEDIATYPE_SD;
u8 n3ds = false;
if(R_SUCCEEDED(APT_CheckNew3DS(&n3ds)) && !n3ds && ((titleId >> 28) & 0xF) == 2) {
return R_FBI_WRONG_SYSTEM;
}
// Deleting FBI before it reinstalls itself causes issues.
if(((titleId >> 8) & 0xFFFFF) != 0xF8001) {
AM_DeleteTitle(dest, titleId);
AM_DeleteTicket(titleId);
if(dest == 1) {
AM_QueryAvailableExternalTitleDatabase(NULL);
}
}
Result res = AM_StartCiaInstall(dest, handle);
if(R_SUCCEEDED(res)) {
qrInstallData->currTitleId = titleId;
}
return res;
}
static Result qrinstall_close_dst(void* data, u32 index, bool succeeded, u32 handle) {
if(succeeded) {
qr_install_data* qrInstallData = (qr_install_data*) data;
Result res = 0;
if(R_SUCCEEDED(res = AM_FinishCiaInstall(handle))) {
if(qrInstallData->currTitleId == 0x0004013800000002 || qrInstallData->currTitleId == 0x0004013820000002) {
res = AM_InstallFirm(qrInstallData->currTitleId);
}
}
return res;
} else {
return AM_CancelCIAInstall(handle);
}
}
static Result qrinstall_write_dst(void* data, u32 handle, u32* bytesWritten, void* buffer, u64 offset, u32 size) {
return FSFILE_Write(handle, bytesWritten, offset, buffer, size, 0);
}
static bool qrinstall_error(void* data, u32 index, Result res) {
qr_install_data* qrInstallData = (qr_install_data*) data;
if(res == R_FBI_CANCELLED) {
prompt_display("Failure", "Install cancelled.", COLOR_TEXT, false, NULL, NULL, NULL, NULL);
return false;
} else {
char* url = qrInstallData->urls[index];
volatile bool dismissed = false;
if(res == R_FBI_WRONG_SYSTEM) {
if(strlen(url) > 48) {
error_display(&dismissed, NULL, NULL, "Failed to install CIA file.\n%.45s...\nAttempted to install N3DS title to O3DS.", url);
} else {
error_display(&dismissed, NULL, NULL, "Failed to install CIA file.\n%.48s\nAttempted to install N3DS title to O3DS.", url);
}
} else if(res == R_FBI_HTTP_RESPONSE_CODE) {
error_display(&dismissed, NULL, NULL, "Failed to install CIA file.\nHTTP server returned response code %d", ((qr_install_data*) data)->responseCode);
} else {
if(strlen(url) > 48) {
error_display_res(&dismissed, NULL, NULL, res, "Failed to install CIA file.\n%.45s...", url);
} else {
error_display_res(&dismissed, NULL, NULL, res, "Failed to install CIA file.\n%.48s", url);
}
}
while(!dismissed) {
svcSleepThread(1000000);
}
}
return index < qrInstallData->installInfo.total - 1;
}
static void qrinstall_install_update(ui_view* view, void* data, float* progress, char* text) {
qr_install_data* qrInstallData = (qr_install_data*) data;
if(qrInstallData->installInfo.finished) {
ui_pop();
info_destroy(view);
if(!qrInstallData->installInfo.premature) {
prompt_display("Success", "Install finished.", COLOR_TEXT, false, data, NULL, NULL, NULL);
}
return;
}
if(hidKeysDown() & KEY_B) {
svcSignalEvent(qrInstallData->installCancelEvent);
}
*progress = qrInstallData->installInfo.currTotal != 0 ? (float) ((double) qrInstallData->installInfo.currProcessed / (double) qrInstallData->installInfo.currTotal) : 0;
snprintf(text, PROGRESS_TEXT_MAX, "%lu / %lu\n%.2f MB / %.2f MB", qrInstallData->installInfo.processed, qrInstallData->installInfo.total, qrInstallData->installInfo.currProcessed / 1024.0 / 1024.0, qrInstallData->installInfo.currTotal / 1024.0 / 1024.0);
}
static void qrinstall_confirm_onresponse(ui_view* view, void* data, bool response) {
qr_install_data* qrInstallData = (qr_install_data*) data;
if(response) {
qrInstallData->installCancelEvent = task_data_op(&qrInstallData->installInfo);
if(qrInstallData->installCancelEvent != 0) {
info_display("Installing CIA(s)", "Press B to cancel.", true, data, qrinstall_install_update, NULL);
} else {
error_display(NULL, NULL, NULL, "Failed to initiate CIA installation.");
}
}
}
static void qrinstall_free_data(qr_install_data* data) {
if(data->camCancelEvent != 0) {
svcSignalEvent(data->camCancelEvent);
while(svcWaitSynchronization(data->camCancelEvent, 0) == 0) {
svcSleepThread(1000000);
}
data->camCancelEvent = 0;
}
if(data->qrContext != NULL) {
quirc_destroy(data->qrContext);
data->qrContext = NULL;
}
if(data->buffer != NULL) {
free(data->buffer);
data->buffer = NULL;
}
if(data->tex != 0) {
screen_unload_texture(data->tex);
data->tex = 0;
}
free(data);
}
static void qrinstall_wait_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
qr_install_data* qrInstallData = (qr_install_data*) data;
if(qrInstallData->tex != 0) {
screen_draw_texture(qrInstallData->tex, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
}
}
static void qrinstall_wait_update(ui_view* view, void* data, float* progress, char* text) {
qr_install_data* qrInstallData = (qr_install_data*) data;
if(hidKeysDown() & KEY_B) {
ui_pop();
info_destroy(view);
qrinstall_free_data(qrInstallData);
return;
}
if(qrInstallData->tex != 0) {
screen_unload_texture(qrInstallData->tex);
qrInstallData->tex = 0;
}
int w = 0;
int h = 0;
uint8_t* qrBuf = quirc_begin(qrInstallData->qrContext, &w, &h);
svcWaitSynchronization(qrInstallData->mutex, U64_MAX);
qrInstallData->tex = screen_load_texture_auto(qrInstallData->buffer, IMAGE_WIDTH * IMAGE_HEIGHT * sizeof(u16), IMAGE_WIDTH, IMAGE_HEIGHT, GPU_RGB565, false);
for(int x = 0; x < w; x++) {
for(int y = 0; y < h; y++) {
u16 px = qrInstallData->buffer[y * IMAGE_WIDTH + x];
qrBuf[y * w + x] = (u8) (((((px >> 11) & 0x1F) << 3) + (((px >> 5) & 0x3F) << 2) + ((px & 0x1F) << 3)) / 3);
}
}
svcReleaseMutex(qrInstallData->mutex);
quirc_end(qrInstallData->qrContext);
int qrCount = quirc_count(qrInstallData->qrContext);
for(int i = 0; i < qrCount; i++) {
struct quirc_code qrCode;
quirc_extract(qrInstallData->qrContext, i, &qrCode);
struct quirc_data qrData;
quirc_decode_error_t err = quirc_decode(&qrCode, &qrData);
if(err == 0) {
qrInstallData->installInfo.total = 0;
char* currStart = (char*) qrData.payload;
char* currEnd = NULL;
while((currEnd = strchr(currStart, '\n')) != NULL) {
u32 len = currEnd - currStart;
if(len > URL_MAX) {
len = URL_MAX;
}
strncpy(qrInstallData->urls[qrInstallData->installInfo.total++], currStart, len);
currStart = currEnd + 1;
}
if(*currStart != '\0') {
strncpy(qrInstallData->urls[qrInstallData->installInfo.total++], currStart, URL_MAX);
}
prompt_display("Confirmation", "Install from the scanned URL(s)?", COLOR_TEXT, true, data, NULL, NULL, qrinstall_confirm_onresponse);
}
}
snprintf(text, PROGRESS_TEXT_MAX, "Waiting for QR code...");
}
void qrinstall_open() {
qr_install_data* data = (qr_install_data*) calloc(1, sizeof(qr_install_data));
data->qrContext = quirc_new();
if(data->qrContext == NULL) {
error_display(NULL, NULL, NULL, "Failed to create QR context.");
qrinstall_free_data(data);
return;
}
if(quirc_resize(data->qrContext, IMAGE_WIDTH, IMAGE_HEIGHT) != 0) {
error_display(NULL, NULL, NULL, "Failed to resize QR context.");
qrinstall_free_data(data);
return;
}
data->buffer = (u16*) calloc(1, IMAGE_WIDTH * IMAGE_HEIGHT * sizeof(u16));
if(data->buffer == NULL) {
error_display(NULL, NULL, NULL, "Failed to create image buffer.");
qrinstall_free_data(data);
return;
}
data->camCancelEvent = task_capture_cam(&data->mutex, data->buffer, IMAGE_WIDTH, IMAGE_HEIGHT);
if(data->camCancelEvent == 0) {
error_display(NULL, NULL, NULL, "Failed to start camera capture.");
qrinstall_free_data(data);
return;
}
data->tex = 0;
data->currTitleId = 0;
data->installInfo.data = data;
data->installInfo.op = DATAOP_COPY;
data->installInfo.copyEmpty = false;
data->installInfo.total = 0;
data->installInfo.isSrcDirectory = qrinstall_is_src_directory;
data->installInfo.makeDstDirectory = qrinstall_make_dst_directory;
data->installInfo.openSrc = qrinstall_open_src;
data->installInfo.closeSrc = qrinstall_close_src;
data->installInfo.getSrcSize = qrinstall_get_src_size;
data->installInfo.readSrc = qrinstall_read_src;
data->installInfo.openDst = qrinstall_open_dst;
data->installInfo.closeDst = qrinstall_close_dst;
data->installInfo.writeDst = qrinstall_write_dst;
data->installInfo.error = qrinstall_error;
data->installCancelEvent = 0;
info_display("QR Code Install", "B: Return", false, data, qrinstall_wait_update, qrinstall_wait_draw_top);
}

View File

@ -12,6 +12,7 @@ void files_open_twl_photo();
void files_open_twl_sound();
void networkinstall_open();
void pendingtitles_open();
void qrinstall_open();
void systemsavedata_open();
void tickets_open();
void titles_open();

View File

@ -95,6 +95,7 @@ static void systemsavedata_update(ui_view* view, void* data, list_item** items,
ui_pop();
list_destroy(view);
task_clear_system_save_data(listData->items, &listData->count);
free(listData);
return;
}

View File

@ -0,0 +1,163 @@
#include <malloc.h>
#include <string.h>
#include <3ds.h>
#include "../../list.h"
#include "../../error.h"
#include "task.h"
#define EVENT_CANCEL 0
#define EVENT_RECV 1
#define EVENT_BUFFER_ERROR 2
#define EVENT_COUNT 3
typedef struct {
u16* buffer;
s16 width;
s16 height;
Handle mutex;
Handle cancelEvent;
} capture_cam_data;
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())) {
if(R_SUCCEEDED(res = CAMU_SetSize(SELECT_OUT1, SIZE_VGA, CONTEXT_A))
&& R_SUCCEEDED(res = CAMU_SetOutputFormat(SELECT_OUT1, OUTPUT_RGB_565, CONTEXT_A))
&& R_SUCCEEDED(res = CAMU_SetFrameRate(SELECT_OUT1, FRAME_RATE_30))
&& R_SUCCEEDED(res = CAMU_SetNoiseFilter(SELECT_OUT1, true))
&& R_SUCCEEDED(res = CAMU_SetAutoExposure(SELECT_OUT1, true))
&& R_SUCCEEDED(res = CAMU_SetAutoWhiteBalance(SELECT_OUT1, true))
&& R_SUCCEEDED(res = CAMU_Activate(SELECT_OUT1))) {
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, 640, 480))
&& 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)) {
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);
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_FBI_OUT_OF_MEMORY;
}
if(R_FAILED(res)) {
error_display_res(NULL, NULL, NULL, res, "Error capturing camera image.");
}
for(int i = 0; i < EVENT_COUNT; i++) {
if(events[i] != 0) {
svcCloseHandle(events[i]);
events[i] = 0;
}
}
svcCloseHandle(data->mutex);
free(data);
}
Handle task_capture_cam(Handle* mutex, u16* buffer, s16 width, s16 height) {
if(buffer == NULL || width <= 0 || width > 640 || height <= 0 || height > 480 || mutex == 0) {
return 0;
}
capture_cam_data* data = (capture_cam_data*) calloc(1, sizeof(capture_cam_data));
data->buffer = buffer;
data->width = width;
data->height = height;
Result eventRes = svcCreateEvent(&data->cancelEvent, 1);
if(R_FAILED(eventRes)) {
error_display_res(NULL, NULL, NULL, eventRes, "Failed to create camera capture cancel event.");
free(data);
return 0;
}
Result mutexRes = svcCreateMutex(&data->mutex, false);
if(R_FAILED(mutexRes)) {
error_display_res(NULL, NULL, NULL, mutexRes, "Failed to create camera capture buffer mutex.");
svcCloseHandle(data->cancelEvent);
free(data);
return 0;
}
if(threadCreate(task_capture_cam_thread, data, 0x4000, 0x19, 1, true) == NULL) {
error_display(NULL, NULL, NULL, "Failed to create camera capture thread.");
svcCloseHandle(data->mutex);
svcCloseHandle(data->cancelEvent);
free(data);
return 0;
}
*mutex = data->mutex;
return data->cancelEvent;
}

View File

@ -30,7 +30,7 @@ static Result task_populate_ext_save_data_from(populate_ext_save_data_data* data
qsort(extSaveDataIds, extSaveDataCount, sizeof(u64), util_compare_u64);
SMDH smdh;
for(u32 i = 0; i < extSaveDataCount && i < data->max; i++) {
for(u32 i = 0; i < extSaveDataCount && i < data->max && R_SUCCEEDED(res); i++) {
if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) {
break;
}
@ -40,6 +40,7 @@ static Result task_populate_ext_save_data_from(populate_ext_save_data_data* data
extSaveDataInfo->mediaType = mediaType;
extSaveDataInfo->extSaveDataId = extSaveDataIds[i];
extSaveDataInfo->shared = mediaType == MEDIATYPE_NAND;
extSaveDataInfo->hasMeta = false;
list_item* item = &data->items[*data->count];
@ -51,13 +52,11 @@ static Result task_populate_ext_save_data_from(populate_ext_save_data_data* data
utf16_to_utf8((uint8_t*) item->name, smdh.titles[systemLanguage].shortDescription, NAME_MAX);
extSaveDataInfo->hasSmdh = true;
utf16_to_utf8((uint8_t*) extSaveDataInfo->smdhInfo.shortDescription, smdh.titles[systemLanguage].shortDescription, sizeof(extSaveDataInfo->smdhInfo.shortDescription));
utf16_to_utf8((uint8_t*) extSaveDataInfo->smdhInfo.longDescription, smdh.titles[systemLanguage].longDescription, sizeof(extSaveDataInfo->smdhInfo.longDescription));
utf16_to_utf8((uint8_t*) extSaveDataInfo->smdhInfo.publisher, smdh.titles[systemLanguage].publisher, sizeof(extSaveDataInfo->smdhInfo.publisher));
extSaveDataInfo->smdhInfo.texture = screen_load_texture_tiled_auto(smdh.largeIcon, sizeof(smdh.largeIcon), 48, 48, GPU_RGB565, false);
} else {
extSaveDataInfo->hasSmdh = false;
extSaveDataInfo->hasMeta = true;
utf16_to_utf8((uint8_t*) extSaveDataInfo->meta.shortDescription, smdh.titles[systemLanguage].shortDescription, sizeof(extSaveDataInfo->meta.shortDescription));
utf16_to_utf8((uint8_t*) extSaveDataInfo->meta.longDescription, smdh.titles[systemLanguage].longDescription, sizeof(extSaveDataInfo->meta.longDescription));
utf16_to_utf8((uint8_t*) extSaveDataInfo->meta.publisher, smdh.titles[systemLanguage].publisher, sizeof(extSaveDataInfo->meta.publisher));
extSaveDataInfo->meta.texture = screen_load_texture_tiled_auto(smdh.largeIcon, sizeof(smdh.largeIcon), 48, 48, GPU_RGB565, false);
}
bool empty = strlen(item->name) == 0;
@ -88,6 +87,8 @@ static Result task_populate_ext_save_data_from(populate_ext_save_data_data* data
item->data = extSaveDataInfo;
(*data->count)++;
} else {
res = R_FBI_OUT_OF_MEMORY;
}
}
}
@ -112,7 +113,7 @@ static void task_populate_ext_save_data_thread(void* arg) {
free(data);
}
static void task_clear_ext_save_data(list_item* items, u32* count) {
void task_clear_ext_save_data(list_item* items, u32* count) {
if(items == NULL || count == NULL || *count == 0) {
return;
}
@ -123,8 +124,8 @@ static void task_clear_ext_save_data(list_item* items, u32* count) {
for(u32 i = 0; i < prevCount; i++) {
if(items[i].data != NULL) {
ext_save_data_info* extSaveDataInfo = (ext_save_data_info*) items[i].data;
if(extSaveDataInfo->hasSmdh) {
screen_unload_texture(extSaveDataInfo->smdhInfo.texture);
if(extSaveDataInfo->hasMeta) {
screen_unload_texture(extSaveDataInfo->meta.texture);
}
free(items[i].data);

View File

@ -29,9 +29,8 @@ static void task_populate_files_thread(void* arg) {
Result res = 0;
if(data->max > *data->count) {
FS_Path* fsPath = util_make_path_utf8(data->dir->path);
FS_Path* fsPath = util_make_path_utf8(data->dir->path);
if(fsPath != NULL) {
Handle dirHandle = 0;
if(R_SUCCEEDED(res = FSUSER_OpenDirectory(&dirHandle, *data->dir->archive, *fsPath))) {
u32 entryCount = 0;
@ -41,7 +40,7 @@ static void task_populate_files_thread(void* arg) {
qsort(entries, entryCount, sizeof(FS_DirectoryEntry), util_compare_directory_entries);
SMDH smdh;
for(u32 i = 0; i < entryCount && i < data->max; i++) {
for(u32 i = 0; i < entryCount && i < data->max && R_SUCCEEDED(res); i++) {
if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) {
break;
}
@ -78,68 +77,71 @@ static void task_populate_files_thread(void* arg) {
fileInfo->isCia = false;
FS_Path* fileFsPath = util_make_path_utf8(fileInfo->path);
if(fileFsPath != NULL) {
Handle fileHandle;
if(R_SUCCEEDED(FSUSER_OpenFile(&fileHandle, *data->dir->archive, *fileFsPath, FS_OPEN_READ, 0))) {
FSFILE_GetSize(fileHandle, &fileInfo->size);
Handle fileHandle;
if(R_SUCCEEDED(FSUSER_OpenFile(&fileHandle, *data->dir->archive, *fileFsPath, FS_OPEN_READ, 0))) {
FSFILE_GetSize(fileHandle, &fileInfo->size);
size_t len = strlen(fileInfo->path);
if(len > 4) {
if(strcasecmp(&fileInfo->path[len - 4], ".cia") == 0) {
AM_TitleEntry titleEntry;
if(R_SUCCEEDED(AM_GetCiaFileInfo(MEDIATYPE_SD, &titleEntry, fileHandle))) {
data->dir->containsCias = true;
size_t len = strlen(fileInfo->path);
if(len > 4) {
if(strcasecmp(&fileInfo->path[len - 4], ".cia") == 0) {
AM_TitleEntry titleEntry;
if(R_SUCCEEDED(AM_GetCiaFileInfo(MEDIATYPE_SD, &titleEntry, fileHandle))) {
data->dir->containsCias = true;
fileInfo->isCia = true;
fileInfo->ciaInfo.titleId = titleEntry.titleID;
fileInfo->ciaInfo.version = titleEntry.version;
fileInfo->ciaInfo.installedSize = titleEntry.size;
fileInfo->ciaInfo.hasSmdh = false;
if(((titleEntry.titleID >> 32) & 0x8010) != 0 && R_SUCCEEDED(AM_GetCiaFileInfo(MEDIATYPE_NAND, &titleEntry, fileHandle))) {
fileInfo->isCia = true;
fileInfo->ciaInfo.titleId = titleEntry.titleID;
fileInfo->ciaInfo.version = titleEntry.version;
fileInfo->ciaInfo.installedSize = titleEntry.size;
fileInfo->ciaInfo.hasMeta = false;
if(((titleEntry.titleID >> 32) & 0x8010) != 0 && R_SUCCEEDED(AM_GetCiaFileInfo(MEDIATYPE_NAND, &titleEntry, fileHandle))) {
fileInfo->ciaInfo.installedSize = titleEntry.size;
}
if(R_SUCCEEDED(AM_GetCiaIcon(&smdh, fileHandle))) {
u8 systemLanguage = CFG_LANGUAGE_EN;
CFGU_GetSystemLanguage(&systemLanguage);
fileInfo->ciaInfo.hasMeta = true;
utf16_to_utf8((uint8_t*) fileInfo->ciaInfo.meta.shortDescription, smdh.titles[systemLanguage].shortDescription, sizeof(fileInfo->ciaInfo.meta.shortDescription));
utf16_to_utf8((uint8_t*) fileInfo->ciaInfo.meta.longDescription, smdh.titles[systemLanguage].longDescription, sizeof(fileInfo->ciaInfo.meta.longDescription));
utf16_to_utf8((uint8_t*) fileInfo->ciaInfo.meta.publisher, smdh.titles[systemLanguage].publisher, sizeof(fileInfo->ciaInfo.meta.publisher));
fileInfo->ciaInfo.meta.texture = screen_load_texture_tiled_auto(smdh.largeIcon, sizeof(smdh.largeIcon), 48, 48, GPU_RGB565, false);
}
}
} else if(strcasecmp(&fileInfo->path[len - 4], ".tik") == 0) {
u32 bytesRead = 0;
if(R_SUCCEEDED(AM_GetCiaIcon(&smdh, fileHandle))) {
u8 systemLanguage = CFG_LANGUAGE_EN;
CFGU_GetSystemLanguage(&systemLanguage);
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;
fileInfo->ciaInfo.hasSmdh = true;
utf16_to_utf8((uint8_t*) fileInfo->ciaInfo.smdhInfo.shortDescription, smdh.titles[systemLanguage].shortDescription, sizeof(fileInfo->ciaInfo.smdhInfo.shortDescription));
utf16_to_utf8((uint8_t*) fileInfo->ciaInfo.smdhInfo.longDescription, smdh.titles[systemLanguage].longDescription, sizeof(fileInfo->ciaInfo.smdhInfo.longDescription));
utf16_to_utf8((uint8_t*) fileInfo->ciaInfo.smdhInfo.publisher, smdh.titles[systemLanguage].publisher, sizeof(fileInfo->ciaInfo.smdhInfo.publisher));
fileInfo->ciaInfo.smdhInfo.texture = screen_load_texture_tiled_auto(smdh.largeIcon, sizeof(smdh.largeIcon), 48, 48, GPU_RGB565, false);
}
}
} else if(strcasecmp(&fileInfo->path[len - 4], ".tik") == 0) {
u32 bytesRead = 0;
u64 titleId = 0;
if(R_SUCCEEDED(FSFILE_Read(fileHandle, &bytesRead, dataOffsets[sigType] + titleIdOffset, &titleId, sizeof(titleId))) && bytesRead == sizeof(titleId)) {
data->dir->containsTickets = true;
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)) {
data->dir->containsTickets = true;
fileInfo->isTicket = true;
fileInfo->ticketInfo.ticketId = __builtin_bswap64(titleId);
fileInfo->isTicket = true;
fileInfo->ticketInfo.ticketId = __builtin_bswap64(titleId);
}
}
}
}
FSFILE_Close(fileHandle);
}
FSFILE_Close(fileHandle);
util_free_path_utf8(fileFsPath);
}
util_free_path_utf8(fileFsPath);
}
strncpy(item->name, entryName, NAME_MAX);
item->data = fileInfo;
(*data->count)++;
} else {
res = R_FBI_OUT_OF_MEMORY;
}
}
}
@ -153,6 +155,8 @@ static void task_populate_files_thread(void* arg) {
}
util_free_path_utf8(fsPath);
} else {
res = R_FBI_OUT_OF_MEMORY;
}
if(R_FAILED(res)) {
@ -163,7 +167,7 @@ static void task_populate_files_thread(void* arg) {
free(data);
}
static void task_clear_files(list_item* items, u32* count) {
void task_clear_files(list_item* items, u32* count) {
if(items == NULL || count == NULL) {
return;
}
@ -174,8 +178,8 @@ static void task_clear_files(list_item* items, u32* count) {
for(u32 i = 0; i < prevCount; i++) {
if(items[i].data != NULL) {
file_info* fileInfo = (file_info*) items[i].data;
if(fileInfo->isCia && fileInfo->ciaInfo.hasSmdh) {
screen_unload_texture(fileInfo->ciaInfo.smdhInfo.texture);
if(fileInfo->isCia && fileInfo->ciaInfo.hasMeta) {
screen_unload_texture(fileInfo->ciaInfo.meta.texture);
}
free(items[i].data);

View File

@ -33,7 +33,7 @@ static Result task_populate_pending_titles_from(populate_pending_titles_data* da
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 && i < data->max; i++) {
for(u32 i = 0; i < pendingTitleCount && i < data->max && R_SUCCEEDED(res); i++) {
if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) {
break;
}
@ -55,6 +55,8 @@ static Result task_populate_pending_titles_from(populate_pending_titles_data* da
item->data = pendingTitleInfo;
(*data->count)++;
} else {
res = R_FBI_OUT_OF_MEMORY;
}
}
}
@ -86,7 +88,7 @@ static void task_populate_pending_titles_thread(void* arg) {
free(data);
}
static void task_clear_pending_titles(list_item* items, u32* count) {
void task_clear_pending_titles(list_item* items, u32* count) {
if(items == NULL || count == NULL) {
return;
}

View File

@ -31,7 +31,7 @@ static void task_populate_system_save_data_thread(void* arg) {
if(R_SUCCEEDED(res = FSUSER_EnumerateSystemSaveData(&systemSaveDataCount, data->max * sizeof(u32), systemSaveDataIds))) {
qsort(systemSaveDataIds, systemSaveDataCount, sizeof(u32), util_compare_u32);
for(u32 i = 0; i < systemSaveDataCount && i < data->max; i++) {
for(u32 i = 0; i < systemSaveDataCount && i < data->max && R_SUCCEEDED(res); i++) {
if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) {
break;
}
@ -46,6 +46,8 @@ static void task_populate_system_save_data_thread(void* arg) {
item->data = systemSaveDataInfo;
(*data->count)++;
} else {
res = R_FBI_OUT_OF_MEMORY;
}
}
}
@ -63,7 +65,7 @@ static void task_populate_system_save_data_thread(void* arg) {
free(data);
}
static void task_clear_system_save_data(list_item* items, u32* count) {
void task_clear_system_save_data(list_item* items, u32* count) {
if(items == NULL || count == NULL) {
return;
}

View File

@ -32,7 +32,7 @@ static void task_populate_tickets_thread(void* arg) {
if(R_SUCCEEDED(res = AM_GetTicketList(&ticketCount, ticketCount, 0, ticketIds))) {
qsort(ticketIds, ticketCount, sizeof(u64), util_compare_u64);
for(u32 i = 0; i < ticketCount && i < data->max; i++) {
for(u32 i = 0; i < ticketCount && i < data->max && R_SUCCEEDED(res); i++) {
if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) {
break;
}
@ -47,6 +47,8 @@ static void task_populate_tickets_thread(void* arg) {
item->data = ticketInfo;
(*data->count)++;
} else {
res = R_FBI_OUT_OF_MEMORY;
}
}
}
@ -65,7 +67,7 @@ static void task_populate_tickets_thread(void* arg) {
free(data);
}
static void task_clear_tickets(list_item* items, u32* count) {
void task_clear_tickets(list_item* items, u32* count) {
if(items == NULL || count == NULL) {
return;
}

View File

@ -5,6 +5,7 @@
#include <string.h>
#include <3ds.h>
#include <3ds/services/am.h>
#include "../../list.h"
#include "../../error.h"
@ -20,6 +21,229 @@ typedef struct {
Handle cancelEvent;
} populate_titles_data;
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))) {
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;
list_item* item = &data->items[*data->count];
static const u32 filePathData[] = {0x00000000, 0x00000000, 0x00000002, 0x6E6F6369, 0x00000000};
static const FS_Path filePath = (FS_Path) {PATH_BINARY, 0x14, (u8*) filePathData};
u32 archivePath[] = {(u32) (titleId & 0xFFFFFFFF), (u32) ((titleId >> 32) & 0xFFFFFFFF), mediaType, 0x00000000};
FS_Archive archive = {ARCHIVE_SAVEDATA_AND_CONTENT, (FS_Path) {PATH_BINARY, 0x10, (u8*) archivePath}};
Handle fileHandle;
if(R_SUCCEEDED(FSUSER_OpenFileDirectly(&fileHandle, archive, filePath, FS_OPEN_READ, 0))) {
SMDH* smdh = (SMDH*) calloc(1, sizeof(SMDH));
if(smdh != NULL) {
u32 bytesRead;
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;
u8 systemLanguage = CFG_LANGUAGE_EN;
CFGU_GetSystemLanguage(&systemLanguage);
utf16_to_utf8((uint8_t*) item->name, smdh->titles[systemLanguage].shortDescription, NAME_MAX);
utf16_to_utf8((uint8_t*) titleInfo->meta.shortDescription, smdh->titles[systemLanguage].shortDescription, sizeof(titleInfo->meta.shortDescription));
utf16_to_utf8((uint8_t*) titleInfo->meta.longDescription, smdh->titles[systemLanguage].longDescription, sizeof(titleInfo->meta.longDescription));
utf16_to_utf8((uint8_t*) titleInfo->meta.publisher, smdh->titles[systemLanguage].publisher, sizeof(titleInfo->meta.publisher));
titleInfo->meta.texture = screen_load_texture_tiled_auto(smdh->largeIcon, sizeof(smdh->largeIcon), 48, 48, GPU_RGB565, false);
}
}
free(smdh);
}
FSFILE_Close(fileHandle);
}
bool empty = strlen(item->name) == 0;
if(!empty) {
empty = true;
char* curr = item->name;
while(*curr) {
if(*curr != ' ') {
empty = false;
break;
}
curr++;
}
}
if(empty) {
snprintf(item->name, NAME_MAX, "%016llX", titleId);
}
if(mediaType == MEDIATYPE_NAND) {
item->rgba = COLOR_NAND;
} else if(mediaType == MEDIATYPE_SD) {
item->rgba = COLOR_SD;
} else if(mediaType == MEDIATYPE_GAME_CARD) {
item->rgba = COLOR_GAME_CARD;
}
item->data = titleInfo;
(*data->count)++;
} else {
res = R_FBI_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[12] = {'\0'};
u16 version = 0;
u64 installedSize = 0;
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 {
u8* header = (u8*) calloc(1, 0x3B4);
if(header != NULL) {
if(R_SUCCEEDED(res = FSUSER_GetLegacyRomHeader(mediaType, titleId, header))) {
realTitleId = titleId != 0 ? titleId : *(u64*) &header[0x230];
memcpy(productCode, header, 0x00C);
version = header[0x01E];
installedSize = (header[0x012] & 0x2) != 0 ? *(u32*) &header[0x210] : *(u32*) &header[0x080];
}
free(header);
} else {
res = R_FBI_OUT_OF_MEMORY;
}
}
if(R_SUCCEEDED(res)) {
title_info* titleInfo = (title_info*) calloc(1, sizeof(title_info));
if(titleInfo != NULL) {
titleInfo->mediaType = mediaType;
titleInfo->titleId = realTitleId;
strncpy(titleInfo->productCode, productCode, 12);
titleInfo->version = version;
titleInfo->installedSize = installedSize;
titleInfo->twl = true;
titleInfo->hasMeta = false;
list_item* item = &data->items[*data->count];
BNR* bnr = (BNR*) calloc(1, sizeof(BNR));
if(bnr != NULL) {
if(R_SUCCEEDED(FSUSER_GetLegacyBannerData(mediaType, titleId, (u8*) bnr))) {
titleInfo->hasMeta = true;
u8 systemLanguage = CFG_LANGUAGE_EN;
CFGU_GetSystemLanguage(&systemLanguage);
char title[0x100] = {'\0'};
utf16_to_utf8((uint8_t*) title, bnr->titles[systemLanguage], 0x100);
if(strchr(title, '\n') == NULL) {
size_t len = strlen(title);
strncpy(item->name, title, len);
strncpy(titleInfo->meta.shortDescription, title, len);
} else {
char* destinations[] = {titleInfo->meta.shortDescription, titleInfo->meta.longDescription, titleInfo->meta.publisher};
int currDest = 0;
char* last = title;
char* curr = NULL;
while(currDest < 3 && (curr = strchr(last, '\n')) != NULL) {
if(currDest == 0) {
strncpy(item->name, last, curr - last);
}
strncpy(destinations[currDest++], last, curr - last);
last = curr + 1;
}
strncpy(item->name, title, last - title);
if(currDest < 3) {
strncpy(destinations[currDest], last, strlen(title) - (last - title));
}
}
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);
}
}
titleInfo->meta.texture = screen_load_texture_auto(icon, sizeof(icon), 32, 32, GPU_RGBA5551, false);
}
free(bnr);
}
bool empty = strlen(item->name) == 0;
if(!empty) {
empty = true;
char* curr = item->name;
while(*curr) {
if(*curr != ' ') {
empty = false;
break;
}
curr++;
}
}
if(empty) {
snprintf(item->name, NAME_MAX, "%016llX", realTitleId);
}
item->rgba = COLOR_DS_TITLE;
item->data = titleInfo;
(*data->count)++;
} else {
res = R_FBI_OUT_OF_MEMORY;
}
}
return res;
}
static Result task_populate_titles_from(populate_titles_data* data, FS_MediaType mediaType, bool useDSiWare) {
bool inserted;
FS_CardType type;
@ -37,168 +261,17 @@ static Result task_populate_titles_from(populate_titles_data* data, FS_MediaType
if(R_SUCCEEDED(res = AM_GetTitleList(&titleCount, mediaType, titleCount, titleIds))) {
qsort(titleIds, titleCount, sizeof(u64), util_compare_u64);
AM_TitleEntry* titleInfos = (AM_TitleEntry*) calloc(titleCount, sizeof(AM_TitleEntry));
if(titleInfos != NULL) {
if(R_SUCCEEDED(res = AM_GetTitleInfo(mediaType, titleCount, titleIds, titleInfos))) {
SMDH* smdh = (SMDH*) calloc(1, sizeof(SMDH));
BNR* bnr = (BNR*) calloc(1, sizeof(BNR));
for(u32 i = 0; i < titleCount && i < data->max; i++) {
if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) {
break;
}
bool dsiWare = ((titleIds[i] >> 32) & 0x8000) != 0;
if(dsiWare != useDSiWare) {
continue;
}
title_info* titleInfo = (title_info*) calloc(1, sizeof(title_info));
if(titleInfo != NULL) {
titleInfo->mediaType = mediaType;
titleInfo->titleId = titleIds[i];
AM_GetTitleProductCode(mediaType, titleIds[i], titleInfo->productCode);
titleInfo->version = titleInfos[i].version;
titleInfo->installedSize = titleInfos[i].size;
titleInfo->twl = dsiWare;
titleInfo->hasSmdh = false;
list_item* item = &data->items[*data->count];
if(dsiWare) {
if(R_SUCCEEDED(FSUSER_GetLegacyBannerData(mediaType, titleIds[i], (u8*) bnr))) {
titleInfo->hasSmdh = true;
u8 systemLanguage = CFG_LANGUAGE_EN;
CFGU_GetSystemLanguage(&systemLanguage);
char title[0x100] = {'\0'};
utf16_to_utf8((uint8_t*) title, bnr->titles[systemLanguage], 0x100);
if(strchr(title, '\n') == NULL) {
size_t len = strlen(title);
strncpy(item->name, title, len);
strncpy(titleInfo->smdhInfo.shortDescription, title, len);
} else {
char* destinations[] = {titleInfo->smdhInfo.shortDescription, titleInfo->smdhInfo.longDescription, titleInfo->smdhInfo.publisher};
int currDest = 0;
char* last = title;
char* curr = NULL;
while(currDest < 3 && (curr = strchr(last, '\n')) != NULL) {
if(currDest == 0) {
strncpy(item->name, last, curr - last);
}
strncpy(destinations[currDest++], last, curr - last);
last = curr + 1;
}
if(currDest < 3) {
strncpy(destinations[currDest], last, strlen(title) - (last - title));
}
strncpy(item->name, title, last - title);
int len = strlen(item->name);
for(int pos = 0; pos < len; pos++) {
if(item->name[pos] == '\n') {
item->name[pos] = ' ';
}
}
}
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);
}
}
titleInfo->smdhInfo.texture = screen_load_texture_auto(icon, sizeof(icon), 32, 32, GPU_RGBA5551, false);
}
} else {
static const u32 filePathData[] = {0x00000000, 0x00000000, 0x00000002, 0x6E6F6369, 0x00000000};
static const FS_Path filePath = (FS_Path) {PATH_BINARY, 0x14, (u8*) filePathData};
u32 archivePath[] = {(u32) (titleIds[i] & 0xFFFFFFFF), (u32) ((titleIds[i] >> 32) & 0xFFFFFFFF), mediaType, 0x00000000};
FS_Archive archive = {ARCHIVE_SAVEDATA_AND_CONTENT, (FS_Path) {PATH_BINARY, 0x10, (u8*) archivePath}};
Handle fileHandle;
if(R_SUCCEEDED(FSUSER_OpenFileDirectly(&fileHandle, archive, filePath, FS_OPEN_READ, 0))) {
u32 bytesRead;
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') {
u8 systemLanguage = CFG_LANGUAGE_EN;
CFGU_GetSystemLanguage(&systemLanguage);
utf16_to_utf8((uint8_t*) item->name, smdh->titles[systemLanguage].shortDescription, NAME_MAX);
titleInfo->hasSmdh = true;
utf16_to_utf8((uint8_t*) titleInfo->smdhInfo.shortDescription, smdh->titles[systemLanguage].shortDescription, sizeof(titleInfo->smdhInfo.shortDescription));
utf16_to_utf8((uint8_t*) titleInfo->smdhInfo.longDescription, smdh->titles[systemLanguage].longDescription, sizeof(titleInfo->smdhInfo.longDescription));
utf16_to_utf8((uint8_t*) titleInfo->smdhInfo.publisher, smdh->titles[systemLanguage].publisher, sizeof(titleInfo->smdhInfo.publisher));
titleInfo->smdhInfo.texture = screen_load_texture_tiled_auto(smdh->largeIcon, sizeof(smdh->largeIcon), 48, 48, GPU_RGB565, false);
}
}
FSFILE_Close(fileHandle);
}
}
bool empty = strlen(item->name) == 0;
if(!empty) {
empty = true;
char* curr = item->name;
while(*curr) {
if(*curr != ' ') {
empty = false;
break;
}
curr++;
}
}
if(empty) {
snprintf(item->name, NAME_MAX, "%016llX", titleIds[i]);
}
if(mediaType == MEDIATYPE_NAND) {
if(dsiWare) {
item->rgba = COLOR_DS_TITLE;
} else {
item->rgba = COLOR_NAND;
}
} else if(mediaType == MEDIATYPE_SD) {
item->rgba = COLOR_SD;
} else if(mediaType == MEDIATYPE_GAME_CARD) {
item->rgba = COLOR_GAME_CARD;
}
item->data = titleInfo;
(*data->count)++;
}
}
free(smdh);
free(bnr);
for(u32 i = 0; i < titleCount && i < data->max && R_SUCCEEDED(res); i++) {
if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) {
break;
}
free(titleInfos);
} else {
res = R_FBI_OUT_OF_MEMORY;
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]);
}
}
@ -208,84 +281,7 @@ static Result task_populate_titles_from(populate_titles_data* data, FS_MediaType
}
}
} else {
u8* header = (u8*) calloc(1, 0x3B4);
BNR* bnr = (BNR*) calloc(1, sizeof(BNR));
if(R_SUCCEEDED(res = FSUSER_GetLegacyRomHeader(MEDIATYPE_GAME_CARD, 0, header)) && R_SUCCEEDED(res = FSUSER_GetLegacyBannerData(MEDIATYPE_GAME_CARD, 0, (u8*) bnr))) {
title_info* titleInfo = (title_info*) calloc(1, sizeof(title_info));
if(titleInfo != NULL) {
titleInfo->mediaType = MEDIATYPE_GAME_CARD;
titleInfo->titleId = *(u64*) &header[0x230];
memcpy(titleInfo->productCode, header, 0x00C);
titleInfo->version = header[0x01E];
titleInfo->installedSize = titleInfo->titleId != 0 ? *(u32*) &header[0x210] : *(u32*) &header[0x080];
titleInfo->twl = true;
titleInfo->hasSmdh = true;
list_item* item = &data->items[*data->count];
u8 systemLanguage = CFG_LANGUAGE_EN;
CFGU_GetSystemLanguage(&systemLanguage);
char title[0x100] = {'\0'};
utf16_to_utf8((uint8_t*) title, bnr->titles[systemLanguage], 0x100);
if(strchr(title, '\n') == NULL) {
size_t len = strlen(title);
strncpy(item->name, title, len);
strncpy(titleInfo->smdhInfo.shortDescription, title, len);
} else {
char* destinations[] = {titleInfo->smdhInfo.shortDescription, titleInfo->smdhInfo.longDescription, titleInfo->smdhInfo.publisher};
int currDest = 0;
char* last = title;
char* curr = NULL;
while(currDest < 3 && (curr = strchr(last, '\n')) != NULL) {
if(currDest == 0) {
strncpy(item->name, last, curr - last);
}
strncpy(destinations[currDest++], last, curr - last);
last = curr + 1;
}
strncpy(item->name, title, last - title);
if(currDest < 3) {
strncpy(destinations[currDest], last, strlen(title) - (last - title));
}
}
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);
}
}
titleInfo->smdhInfo.texture = screen_load_texture_auto(icon, sizeof(icon), 32, 32, GPU_RGBA5551, false);
item->rgba = COLOR_DS_TITLE;
item->data = titleInfo;
(*data->count)++;
}
}
free(header);
free(bnr);
res = task_populate_titles_add_twl(data, mediaType, 0);
}
return res;
@ -303,7 +299,7 @@ static void task_populate_titles_thread(void* arg) {
free(data);
}
static void task_clear_titles(list_item* items, u32* count) {
void task_clear_titles(list_item* items, u32* count) {
if(items == NULL || count == NULL) {
return;
}
@ -314,8 +310,8 @@ static void task_clear_titles(list_item* items, u32* count) {
for(u32 i = 0; i < prevCount; i++) {
if(items[i].data != NULL) {
title_info* titleInfo = (title_info*) items[i].data;
if(titleInfo->hasSmdh) {
screen_unload_texture(titleInfo->smdhInfo.texture);
if(titleInfo->hasMeta) {
screen_unload_texture(titleInfo->meta.texture);
}
free(items[i].data);

View File

@ -9,7 +9,7 @@ typedef struct {
char longDescription[0x200];
char publisher[0x100];
u32 texture;
} smdh_info;
} meta_info;
typedef struct {
FS_MediaType mediaType;
@ -18,8 +18,8 @@ typedef struct {
u16 version;
u64 installedSize;
bool twl;
bool hasSmdh;
smdh_info smdhInfo;
bool hasMeta;
meta_info meta;
} title_info;
typedef struct {
@ -36,8 +36,8 @@ typedef struct {
FS_MediaType mediaType;
u64 extSaveDataId;
bool shared;
bool hasSmdh;
smdh_info smdhInfo;
bool hasMeta;
meta_info meta;
} ext_save_data_info;
typedef struct {
@ -48,8 +48,8 @@ typedef struct {
u64 titleId;
u16 version;
u64 installedSize;
bool hasSmdh;
smdh_info smdhInfo;
bool hasMeta;
meta_info meta;
} cia_info;
typedef struct {
@ -70,7 +70,8 @@ typedef struct {
#define R_FBI_CANCELLED MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, 1)
#define R_FBI_ERRNO MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 2)
#define R_FBI_WRONG_SYSTEM MAKERESULT(RL_PERMANENT, RS_NOTSUPPORTED, RM_APPLICATION, 3)
#define R_FBI_HTTP_RESPONSE_CODE MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 3)
#define R_FBI_WRONG_SYSTEM MAKERESULT(RL_PERMANENT, RS_NOTSUPPORTED, RM_APPLICATION, 4)
#define R_FBI_OUT_OF_MEMORY MAKERESULT(RL_FATAL, RS_OUTOFRESOURCE, RM_APPLICATION, RD_OUT_OF_MEMORY)
@ -120,10 +121,24 @@ typedef struct {
bool task_is_quit_all();
void task_quit_all();
Handle task_capture_cam(Handle* mutex, u16* buffer, s16 width, s16 height);
Handle task_data_op(data_op_info* info);
void task_clear_ext_save_data(list_item* items, u32* count);
Handle task_populate_ext_save_data(list_item* items, u32* count, u32 max);
void task_clear_files(list_item* items, u32* count);
Handle task_populate_files(list_item* items, u32* count, u32 max, file_info* dir);
void task_clear_pending_titles(list_item* items, u32* count);
Handle task_populate_pending_titles(list_item* items, u32* count, u32 max);
void task_clear_system_save_data(list_item* items, u32* count);
Handle task_populate_system_save_data(list_item* items, u32* count, u32 max);
void task_clear_tickets(list_item* items, u32* count);
Handle task_populate_tickets(list_item* items, u32* count, u32 max);
Handle task_populate_titles(list_item* items, u32* count, u32 max);
Handle task_data_op(data_op_info* info);
void task_clear_titles(list_item* items, u32* count);
Handle task_populate_titles(list_item* items, u32* count, u32 max);

View File

@ -93,6 +93,7 @@ static void tickets_update(ui_view* view, void* data, list_item** items, u32** i
ui_pop();
list_destroy(view);
task_clear_tickets(listData->items, &listData->count);
free(listData);
return;
}

View File

@ -139,6 +139,7 @@ static void titles_update(ui_view* view, void* data, list_item** items, u32** it
ui_pop();
list_destroy(view);
task_clear_titles(listData->items, &listData->count);
free(listData);
return;
}

View File

@ -64,25 +64,10 @@ void ui_pop() {
if(ui_stack_top >= 0) {
ui_stack[ui_stack_top--] = NULL;
}
svcReleaseMutex(ui_stack_mutex);
}
void ui_update() {
hidScanInput();
ui_view* 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);
}
}
static void ui_draw_top(ui_view* ui) {
u32 topScreenBgWidth = 0;
u32 topScreenBgHeight = 0;
@ -232,14 +217,31 @@ static void ui_draw_bottom(ui_view* ui) {
}
}
void ui_draw() {
ui_view* ui = ui_top();
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);
}
ui = ui_top();
if(ui != NULL) {
screen_begin_frame();
ui_draw_top(ui);
ui_draw_bottom(ui);
screen_end_frame();
}
return ui != NULL;
}
void ui_draw_ext_save_data_info(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
@ -247,50 +249,50 @@ void ui_draw_ext_save_data_info(ui_view* view, void* data, float x1, float y1, f
char buf[64];
if(info->hasSmdh) {
u32 smdhInfoBoxShadowWidth;
u32 smdhInfoBoxShadowHeight;
screen_get_texture_size(&smdhInfoBoxShadowWidth, &smdhInfoBoxShadowHeight, TEXTURE_SMDH_INFO_BOX_SHADOW);
if(info->hasMeta) {
u32 metaInfoBoxShadowWidth;
u32 metaInfoBoxShadowHeight;
screen_get_texture_size(&metaInfoBoxShadowWidth, &metaInfoBoxShadowHeight, TEXTURE_META_INFO_BOX_SHADOW);
float smdhInfoBoxShadowX = x1 + (x2 - x1 - smdhInfoBoxShadowWidth) / 2;
float smdhInfoBoxShadowY = y1 + (y2 - y1) / 4 - smdhInfoBoxShadowHeight / 2;
screen_draw_texture(TEXTURE_SMDH_INFO_BOX_SHADOW, smdhInfoBoxShadowX, smdhInfoBoxShadowY, smdhInfoBoxShadowWidth, smdhInfoBoxShadowHeight);
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 smdhInfoBoxWidth;
u32 smdhInfoBoxHeight;
screen_get_texture_size(&smdhInfoBoxWidth, &smdhInfoBoxHeight, TEXTURE_SMDH_INFO_BOX);
u32 metaInfoBoxWidth;
u32 metaInfoBoxHeight;
screen_get_texture_size(&metaInfoBoxWidth, &metaInfoBoxHeight, TEXTURE_META_INFO_BOX);
float smdhInfoBoxX = x1 + (x2 - x1 - smdhInfoBoxWidth) / 2;
float smdhInfoBoxY = y1 + (y2 - y1) / 4 - smdhInfoBoxHeight / 2;
screen_draw_texture(TEXTURE_SMDH_INFO_BOX, smdhInfoBoxX, smdhInfoBoxY, smdhInfoBoxWidth, smdhInfoBoxHeight);
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);
u32 smdhIconWidth;
u32 smdhIconHeight;
screen_get_texture_size(&smdhIconWidth, &smdhIconHeight, info->smdhInfo.texture);
u32 iconWidth;
u32 iconHeight;
screen_get_texture_size(&iconWidth, &iconHeight, info->meta.texture);
float smdhIconX = smdhInfoBoxX + (64 - smdhIconWidth) / 2;
float smdhIconY = smdhInfoBoxY + (smdhInfoBoxHeight - smdhIconHeight) / 2;
screen_draw_texture(info->smdhInfo.texture, smdhIconX, smdhIconY, smdhIconWidth, smdhIconHeight);
float iconX = metaInfoBoxX + (64 - iconWidth) / 2;
float iconY = metaInfoBoxY + (metaInfoBoxHeight - iconHeight) / 2;
screen_draw_texture(info->meta.texture, iconX, iconY, iconWidth, iconHeight);
float shortDescriptionHeight;
screen_get_string_size(NULL, &shortDescriptionHeight, info->smdhInfo.shortDescription, 0.5f, 0.5f);
screen_get_string_size(NULL, &shortDescriptionHeight, info->meta.shortDescription, 0.5f, 0.5f);
float longDescriptionHeight;
screen_get_string_size(NULL, &longDescriptionHeight, info->smdhInfo.longDescription, 0.5f, 0.5f);
screen_get_string_size(NULL, &longDescriptionHeight, info->meta.longDescription, 0.5f, 0.5f);
float publisherHeight;
screen_get_string_size(NULL, &publisherHeight, info->smdhInfo.publisher, 0.5f, 0.5f);
screen_get_string_size(NULL, &publisherHeight, info->meta.publisher, 0.5f, 0.5f);
float smdhTextX = smdhInfoBoxX + 64;
float metaTextX = metaInfoBoxX + 64;
float smdhShortDescriptionY = smdhInfoBoxY + (64 - shortDescriptionHeight - 2 - longDescriptionHeight - 2 - publisherHeight) / 2;
screen_draw_string(info->smdhInfo.shortDescription, smdhTextX, smdhShortDescriptionY, 0.5f, 0.5f, COLOR_TEXT, false);
float shortDescriptionY = metaInfoBoxY + (64 - shortDescriptionHeight - 2 - longDescriptionHeight - 2 - publisherHeight) / 2;
screen_draw_string(info->meta.shortDescription, metaTextX, shortDescriptionY, 0.5f, 0.5f, COLOR_TEXT, false);
float smdhLongDescriptionY = smdhShortDescriptionY + shortDescriptionHeight + 2;
screen_draw_string(info->smdhInfo.longDescription, smdhTextX, smdhLongDescriptionY, 0.5f, 0.5f, COLOR_TEXT, false);
float longDescriptionY = shortDescriptionY + shortDescriptionHeight + 2;
screen_draw_string(info->meta.longDescription, metaTextX, longDescriptionY, 0.5f, 0.5f, COLOR_TEXT, false);
float smdhPublisherY = smdhLongDescriptionY + longDescriptionHeight + 2;
screen_draw_string(info->smdhInfo.publisher, smdhTextX, smdhPublisherY, 0.5f, 0.5f, COLOR_TEXT, false);
float publisherY = longDescriptionY + longDescriptionHeight + 2;
screen_draw_string(info->meta.publisher, metaTextX, publisherY, 0.5f, 0.5f, COLOR_TEXT, false);
}
snprintf(buf, 64, "Ext Save Data ID: %016llX", info->extSaveDataId);
@ -345,50 +347,50 @@ void ui_draw_file_info(ui_view* view, void* data, float x1, float y1, float x2,
screen_draw_string(buf, sizeX, sizeY, 0.5f, 0.5f, COLOR_TEXT, false);
if(info->isCia) {
if(info->ciaInfo.hasSmdh) {
u32 smdhInfoBoxShadowWidth;
u32 smdhInfoBoxShadowHeight;
screen_get_texture_size(&smdhInfoBoxShadowWidth, &smdhInfoBoxShadowHeight, TEXTURE_SMDH_INFO_BOX_SHADOW);
if(info->ciaInfo.hasMeta) {
u32 metaInfoBoxShadowWidth;
u32 metaInfoBoxShadowHeight;
screen_get_texture_size(&metaInfoBoxShadowWidth, &metaInfoBoxShadowHeight, TEXTURE_META_INFO_BOX_SHADOW);
float smdhInfoBoxShadowX = x1 + (x2 - x1 - smdhInfoBoxShadowWidth) / 2;
float smdhInfoBoxShadowY = y1 + (y2 - y1) / 4 - smdhInfoBoxShadowHeight / 2;
screen_draw_texture(TEXTURE_SMDH_INFO_BOX_SHADOW, smdhInfoBoxShadowX, smdhInfoBoxShadowY, smdhInfoBoxShadowWidth, smdhInfoBoxShadowHeight);
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 smdhInfoBoxWidth;
u32 smdhInfoBoxHeight;
screen_get_texture_size(&smdhInfoBoxWidth, &smdhInfoBoxHeight, TEXTURE_SMDH_INFO_BOX);
u32 metaInfoBoxWidth;
u32 metaInfoBoxHeight;
screen_get_texture_size(&metaInfoBoxWidth, &metaInfoBoxHeight, TEXTURE_META_INFO_BOX);
float smdhInfoBoxX = x1 + (x2 - x1 - smdhInfoBoxWidth) / 2;
float smdhInfoBoxY = y1 + (y2 - y1) / 4 - smdhInfoBoxHeight / 2;
screen_draw_texture(TEXTURE_SMDH_INFO_BOX, smdhInfoBoxX, smdhInfoBoxY, smdhInfoBoxWidth, smdhInfoBoxHeight);
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);
u32 smdhIconWidth;
u32 smdhIconHeight;
screen_get_texture_size(&smdhIconWidth, &smdhIconHeight, info->ciaInfo.smdhInfo.texture);
u32 iconWidth;
u32 iconHeight;
screen_get_texture_size(&iconWidth, &iconHeight, info->ciaInfo.meta.texture);
float smdhIconX = smdhInfoBoxX + (64 - smdhIconWidth) / 2;
float smdhIconY = smdhInfoBoxY + (smdhInfoBoxHeight - smdhIconHeight) / 2;
screen_draw_texture(info->ciaInfo.smdhInfo.texture, smdhIconX, smdhIconY, smdhIconWidth, smdhIconHeight);
float iconX = metaInfoBoxX + (64 - iconWidth) / 2;
float iconY = metaInfoBoxY + (metaInfoBoxHeight - iconHeight) / 2;
screen_draw_texture(info->ciaInfo.meta.texture, iconX, iconY, iconWidth, iconHeight);
float shortDescriptionHeight;
screen_get_string_size(NULL, &shortDescriptionHeight, info->ciaInfo.smdhInfo.shortDescription, 0.5f, 0.5f);
screen_get_string_size(NULL, &shortDescriptionHeight, info->ciaInfo.meta.shortDescription, 0.5f, 0.5f);
float longDescriptionHeight;
screen_get_string_size(NULL, &longDescriptionHeight, info->ciaInfo.smdhInfo.longDescription, 0.5f, 0.5f);
screen_get_string_size(NULL, &longDescriptionHeight, info->ciaInfo.meta.longDescription, 0.5f, 0.5f);
float publisherHeight;
screen_get_string_size(NULL, &publisherHeight, info->ciaInfo.smdhInfo.publisher, 0.5f, 0.5f);
screen_get_string_size(NULL, &publisherHeight, info->ciaInfo.meta.publisher, 0.5f, 0.5f);
float smdhTextX = smdhInfoBoxX + 64;
float metaTextX = metaInfoBoxX + 64;
float smdhShortDescriptionY = smdhInfoBoxY + (64 - shortDescriptionHeight - 2 - longDescriptionHeight - 2 - publisherHeight) / 2;
screen_draw_string(info->ciaInfo.smdhInfo.shortDescription, smdhTextX, smdhShortDescriptionY, 0.5f, 0.5f, COLOR_TEXT, false);
float shortDescriptionY = metaInfoBoxY + (64 - shortDescriptionHeight - 2 - longDescriptionHeight - 2 - publisherHeight) / 2;
screen_draw_string(info->ciaInfo.meta.shortDescription, metaTextX, shortDescriptionY, 0.5f, 0.5f, COLOR_TEXT, false);
float smdhLongDescriptionY = smdhShortDescriptionY + shortDescriptionHeight + 2;
screen_draw_string(info->ciaInfo.smdhInfo.longDescription, smdhTextX, smdhLongDescriptionY, 0.5f, 0.5f, COLOR_TEXT, false);
float longDescriptionY = shortDescriptionY + shortDescriptionHeight + 2;
screen_draw_string(info->ciaInfo.meta.longDescription, metaTextX, longDescriptionY, 0.5f, 0.5f, COLOR_TEXT, false);
float smdhPublisherY = smdhLongDescriptionY + longDescriptionHeight + 2;
screen_draw_string(info->ciaInfo.smdhInfo.publisher, smdhTextX, smdhPublisherY, 0.5f, 0.5f, COLOR_TEXT, false);
float publisherY = longDescriptionY + longDescriptionHeight + 2;
screen_draw_string(info->ciaInfo.meta.publisher, metaTextX, publisherY, 0.5f, 0.5f, COLOR_TEXT, false);
}
snprintf(buf, 64, "Title ID: %016llX", info->ciaInfo.titleId);
@ -517,50 +519,50 @@ void ui_draw_title_info(ui_view* view, void* data, float x1, float y1, float x2,
char buf[64];
if(info->hasSmdh) {
u32 smdhInfoBoxShadowWidth;
u32 smdhInfoBoxShadowHeight;
screen_get_texture_size(&smdhInfoBoxShadowWidth, &smdhInfoBoxShadowHeight, TEXTURE_SMDH_INFO_BOX_SHADOW);
if(info->hasMeta) {
u32 metaInfoBoxShadowWidth;
u32 metaInfoBoxShadowHeight;
screen_get_texture_size(&metaInfoBoxShadowWidth, &metaInfoBoxShadowHeight, TEXTURE_META_INFO_BOX_SHADOW);
float smdhInfoBoxShadowX = x1 + (x2 - x1 - smdhInfoBoxShadowWidth) / 2;
float smdhInfoBoxShadowY = y1 + (y2 - y1) / 4 - smdhInfoBoxShadowHeight / 2;
screen_draw_texture(TEXTURE_SMDH_INFO_BOX_SHADOW, smdhInfoBoxShadowX, smdhInfoBoxShadowY, smdhInfoBoxShadowWidth, smdhInfoBoxShadowHeight);
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 smdhInfoBoxWidth;
u32 smdhInfoBoxHeight;
screen_get_texture_size(&smdhInfoBoxWidth, &smdhInfoBoxHeight, TEXTURE_SMDH_INFO_BOX);
u32 metaInfoBoxWidth;
u32 metaInfoBoxHeight;
screen_get_texture_size(&metaInfoBoxWidth, &metaInfoBoxHeight, TEXTURE_META_INFO_BOX);
float smdhInfoBoxX = x1 + (x2 - x1 - smdhInfoBoxWidth) / 2;
float smdhInfoBoxY = y1 + (y2 - y1) / 4 - smdhInfoBoxHeight / 2;
screen_draw_texture(TEXTURE_SMDH_INFO_BOX, smdhInfoBoxX, smdhInfoBoxY, smdhInfoBoxWidth, smdhInfoBoxHeight);
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);
u32 smdhIconWidth;
u32 smdhIconHeight;
screen_get_texture_size(&smdhIconWidth, &smdhIconHeight, info->smdhInfo.texture);
u32 iconWidth;
u32 iconHeight;
screen_get_texture_size(&iconWidth, &iconHeight, info->meta.texture);
float smdhIconX = smdhInfoBoxX + (64 - smdhIconWidth) / 2;
float smdhIconY = smdhInfoBoxY + (smdhInfoBoxHeight - smdhIconHeight) / 2;
screen_draw_texture(info->smdhInfo.texture, smdhIconX, smdhIconY, smdhIconWidth, smdhIconHeight);
float iconX = metaInfoBoxX + (64 - iconWidth) / 2;
float iconY = metaInfoBoxY + (metaInfoBoxHeight - iconHeight) / 2;
screen_draw_texture(info->meta.texture, iconX, iconY, iconWidth, iconHeight);
float shortDescriptionHeight;
screen_get_string_size(NULL, &shortDescriptionHeight, info->smdhInfo.shortDescription, 0.5f, 0.5f);
screen_get_string_size(NULL, &shortDescriptionHeight, info->meta.shortDescription, 0.5f, 0.5f);
float longDescriptionHeight;
screen_get_string_size(NULL, &longDescriptionHeight, info->smdhInfo.longDescription, 0.5f, 0.5f);
screen_get_string_size(NULL, &longDescriptionHeight, info->meta.longDescription, 0.5f, 0.5f);
float publisherHeight;
screen_get_string_size(NULL, &publisherHeight, info->smdhInfo.publisher, 0.5f, 0.5f);
screen_get_string_size(NULL, &publisherHeight, info->meta.publisher, 0.5f, 0.5f);
float smdhTextX = smdhInfoBoxX + 64;
float metaTextX = metaInfoBoxX + 64;
float smdhShortDescriptionY = smdhInfoBoxY + (64 - shortDescriptionHeight - 2 - longDescriptionHeight - 2 - publisherHeight) / 2;
screen_draw_string(info->smdhInfo.shortDescription, smdhTextX, smdhShortDescriptionY, 0.5f, 0.5f, COLOR_TEXT, false);
float shortDescriptionY = metaInfoBoxY + (64 - shortDescriptionHeight - 2 - longDescriptionHeight - 2 - publisherHeight) / 2;
screen_draw_string(info->meta.shortDescription, metaTextX, shortDescriptionY, 0.5f, 0.5f, COLOR_TEXT, false);
float smdhLongDescriptionY = smdhShortDescriptionY + shortDescriptionHeight + 2;
screen_draw_string(info->smdhInfo.longDescription, smdhTextX, smdhLongDescriptionY, 0.5f, 0.5f, COLOR_TEXT, false);
float longDescriptionY = shortDescriptionY + shortDescriptionHeight + 2;
screen_draw_string(info->meta.longDescription, metaTextX, longDescriptionY, 0.5f, 0.5f, COLOR_TEXT, false);
float smdhPublisherY = smdhLongDescriptionY + longDescriptionHeight + 2;
screen_draw_string(info->smdhInfo.publisher, smdhTextX, smdhPublisherY, 0.5f, 0.5f, COLOR_TEXT, false);
float publisherY = longDescriptionY + longDescriptionHeight + 2;
screen_draw_string(info->meta.publisher, metaTextX, publisherY, 0.5f, 0.5f, COLOR_TEXT, false);
}
snprintf(buf, 64, "Title ID: %016llX", info->titleId);

View File

@ -17,8 +17,7 @@ void ui_exit();
ui_view* ui_top();
bool ui_push(ui_view* view);
void ui_pop();
void ui_update();
void ui_draw();
bool ui_update();
void ui_draw_ext_save_data_info(ui_view* view, void* data, float x1, float y1, float x2, float y2);
void ui_draw_file_info(ui_view* view, void* data, float x1, float y1, float x2, float y2);