1 Commits

Author SHA1 Message Date
LiquidFenrir
477d8c784b attempt 1
- get rid of all the calls to abs(delta), though that would probably already be optimized by gcc
- only not allow drawing at the same time as swapping if the swapping happens on visible icons
2018-10-19 19:18:40 +02:00
37 changed files with 1036 additions and 2050 deletions

BIN
.flask/banner.t3x Normal file

Binary file not shown.

BIN
.flask/icon.t3x Normal file

Binary file not shown.

5
.flask/meta.json Normal file
View File

@@ -0,0 +1,5 @@
{
"user": "astronautlevel2",
"name": "Anemone3DS",
"description": "A theme and boot splash manager for the Nintendo 3DS console"
}

View File

@@ -1,39 +0,0 @@
---
name: Bug report
about: Found a bug? Report it here
title: ''
labels: ''
assignees: ''
---
**Description**
A clear and concise description of what the bug is.
**Steps to reproduce**
***If you cannot reproduce the bug, please describe what you were doing in as much detail as possible***
*If you were scanning using the QR scanner, ensure you include a link to the theme you were attempting to download.*
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
Add screenshots of any error screens you encountered. Bug reports without screenshots will take longer to solve, so we recommend you get some!
**Crash dumps**
If your bug causes a crash in Luma3DS, please upload any crash dumps generated. You can find these in `SD:/luma/dumps/armX` (`X = 9 or 11`) with the name Luma3DS's exception handlers give you.
**System information**
*System model* e.g. new3DS XL
*System firmware version* e.g. 11.14.0 (this can be found in the System Settings applet by default)
*Anemone3DS version* e.g. v2.2.0 (found in the bottom-left of Anemone3DS)
*Luma3DS version* e.g. v10.2.1 (found in the menu when you hold Select on boot)
**If you are not on the [latest version of Anemone3DS](https://github.com/astronautlevel2/Anemone3DS/releases/latest), your bug report will likely be closed. Ensure the bug occurs in the latest build!**

View File

@@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -1,10 +1,10 @@
# Main Contributors # Main Contributors
* Alex Taber ([@astronautlevel2](https://github.com/astronautlevel2)) * Alex Taber ([@astronautlevel2](https://github.com/astronautlevel2))
* Théo B. ([@LiquidFenrir](https://github.com/LiquidFenrir)) * [@LiquidFenrir](https://github.com/LiquidFenrir)
* Dawid Eckert ([@daedreth](https://github.com/daedreth)) * Dawid Eckert ([@daedreth](https://github.com/daedreth))
* Dylan G. ([@helloman892](https://github.com/helloman892)) * Dylan G. ([@helloman892](https://github.com/helloman892))
* Nils P. ([@ZetaDesigns](https://github.com/ZetaDesigns)) * Nils P. ([@ZetaDesigns](https://github.com/ZetaDesigns))
* Matt Kenny ([@KennLDN](https://github.com/KennLDN)) * Matt Kenny ([@mattkenster](https://github.com/mattkenster))
# Minor Contributors # Minor Contributors
* Nic ([@Wizzrobes](https://github.com/Wizzrobes)) * Nic ([@Wizzrobes](https://github.com/Wizzrobes))
@@ -15,5 +15,4 @@
* [@uyuiyu](https://github.com/uyuiyu) * [@uyuiyu](https://github.com/uyuiyu)
* Guillaume Gérard ([@GreatWizard](https://github.com/GreatWizard)) * Guillaume Gérard ([@GreatWizard](https://github.com/GreatWizard))
* Joel ([@joel16](https://github.com/joel16)) * Joel ([@joel16](https://github.com/joel16))
* [@thedax](https://github.com/thedax) * [@thedax](https://github.com/thedax)
* [@Wryyyong](https://github.com/Wryyyong)

View File

@@ -89,7 +89,7 @@ CFLAGS := -g -Wall -Wextra -O2 -mword-relocations \
-ffunction-sections \ -ffunction-sections \
$(ARCH) $(ARCH)
CFLAGS += $(INCLUDE) -D__3DS__ -D_GNU_SOURCE -DVERSION="\"$(VERSION)\"" -DUSER_AGENT="\"$(APP_TITLE)/$(VERSION)\"" -DAPP_TITLE="\"$(APP_TITLE)\"" CFLAGS += $(INCLUDE) -DARM11 -D_3DS -D_GNU_SOURCE -DVERSION="\"$(VERSION)\"" -DUSER_AGENT="\"$(APP_TITLE)/$(VERSION)\"" -DAPP_TITLE="\"$(APP_TITLE)\""
ifneq ($(strip $(CITRA_MODE)),) ifneq ($(strip $(CITRA_MODE)),)
CFLAGS += -DCITRA_MODE CFLAGS += -DCITRA_MODE
endif endif

View File

@@ -1,11 +1,13 @@
![# Anemone3DS](https://github.com/astronautlevel2/Anemone3DS/blob/master/meta/banner.png) ![# Anemone3DS](https://github.com/astronautlevel2/Anemone3DS/blob/master/meta/banner.png)
A Theme and Splashscreen Manager for the Nintendo 3DS, written in C.\ A Theme and Splashscreen Manager for the Nintendo 3DS, written in C.\
To-do list here: https://trello.com/b/F1YSa1VK
# Dependencies # Dependencies
* devkitARM, which can be installed following the instructions [here](https://devkitpro.org/wiki/Getting_Started). * devkitARM, which can be installed following the instructions [here](https://devkitpro.org/wiki/Getting_Started).
* jansson, libvorbisidec, libpng, and libarchive, which can be retrieved from [devkitPro pacman](https://devkitpro.org/viewtopic.php?f=13&t=8702). * jansson, libvorbisidec, libpng, and libarchive, which can be retrieved from [devkitPro pacman](https://devkitpro.org/viewtopic.php?f=13&t=8702).
* A recent build of [makerom](https://github.com/profi200/Project_CTR) and the latest release of [bannertool](https://github.com/Steveice10/bannertool). These must be added to your PATH. * A recent build of [makerom](https://github.com/profi200/Project_CTR) and the latest release of [bannertool](https://github.com/Steveice10/bannertool). These must be added to your PATH.
A 64-bit Windows binary of makerom is available [here](https://hm892.s-ul.eu/U0Irkqih).
# Building # Building
First of all, make sure devkitARM is properly installed - `$DEVKITPRO` and `$DEVKITARM` should be set to `/opt/devkitpro` and `$DEVKITPRO/devkitARM`, respectively. First of all, make sure devkitARM is properly installed - `$DEVKITPRO` and `$DEVKITARM` should be set to `/opt/devkitpro` and `$DEVKITPRO/devkitARM`, respectively.

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -30,24 +30,21 @@
#include "common.h" #include "common.h"
typedef struct { typedef struct {
u16* camera_buffer; u16 *camera_buffer;
C2D_Image image;
Handle event_stop; C3D_Tex *tex;
Thread cam_thread, ui_thread; Handle mutex;
volatile bool finished;
LightEvent event_cam_info, event_ui_info; volatile bool success;
Handle cancel;
CondVar cond; Handle started;
LightLock mut;
u32 num_readers_active;
bool writer_waiting;
bool writer_active;
bool any_update;
bool capturing;
struct quirc* context; struct quirc* context;
} qr_data; } qr_data;
bool init_qr(void); bool init_qr(void);
void exit_qr(qr_data *data);
void take_picture(void);
#endif #endif

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -31,7 +31,7 @@
typedef u32 Color; typedef u32 Color;
typedef enum { enum {
COLOR_BACKGROUND, //silver-y black COLOR_BACKGROUND, //silver-y black
COLOR_ACCENT, COLOR_ACCENT,
COLOR_WHITE, COLOR_WHITE,

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -55,9 +55,6 @@ typedef enum {
INSTALL_LOADING_REMOTE_PREVIEW, INSTALL_LOADING_REMOTE_PREVIEW,
INSTALL_LOADING_REMOTE_BGM, INSTALL_LOADING_REMOTE_BGM,
INSTALL_DUMPING_THEME,
INSTALL_DUMPING_ALL_THEMES,
INSTALL_NONE, INSTALL_NONE,
} InstallType; } InstallType;
@@ -84,9 +81,6 @@ typedef enum {
TEXT_INSTALL_LOADING_REMOTE_PREVIEW, TEXT_INSTALL_LOADING_REMOTE_PREVIEW,
TEXT_INSTALL_LOADING_REMOTE_BGM, TEXT_INSTALL_LOADING_REMOTE_BGM,
TEXT_INSTALL_DUMPING_THEME,
TEXT_INSTALL_DUMPING_ALL_THEMES,
// Other text // Other text
TEXT_VERSION, TEXT_VERSION,
@@ -131,7 +125,7 @@ typedef enum {
#define BUTTONS_INFO_LINES 4 #define BUTTONS_INFO_LINES 4
#define BUTTONS_INFO_COLUNMNS 2 #define BUTTONS_INFO_COLUNMNS 2
typedef enum { enum {
BUTTONS_Y_INFO = BUTTONS_START_Y+5, BUTTONS_Y_INFO = BUTTONS_START_Y+5,
BUTTONS_Y_LINE_1 = BUTTONS_START_Y + BUTTONS_STEP*1, BUTTONS_Y_LINE_1 = BUTTONS_START_Y + BUTTONS_STEP*1,
@@ -162,7 +156,7 @@ void start_frame(void);
void end_frame(void); void end_frame(void);
void set_screen(C3D_RenderTarget * screen); void set_screen(C3D_RenderTarget * screen);
void throw_error(const char* error, ErrorLevel level); void throw_error(char* error, ErrorLevel level);
bool draw_confirm(const char* conf_msg, Entry_List_s* list); bool draw_confirm(const char* conf_msg, Entry_List_s* list);
void draw_preview(C2D_Image preview, int preview_offset); void draw_preview(C2D_Image preview, int preview_offset);
@@ -179,4 +173,4 @@ void draw_base_interface(void);
void draw_grid_interface(Entry_List_s* list, Instructions_s instructions); void draw_grid_interface(Entry_List_s* list, Instructions_s instructions);
void draw_interface(Entry_List_s* list, Instructions_s instructions); void draw_interface(Entry_List_s* list, Instructions_s instructions);
#endif #endif

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -29,11 +29,9 @@
#include "common.h" #include "common.h"
#define ILLEGAL_CHARS "><\"?;:/\\+,.|[=]" FS_Archive ArchiveSD;
FS_Archive ArchiveHomeExt;
extern FS_Archive ArchiveSD; FS_Archive ArchiveThemeExt;
extern FS_Archive ArchiveHomeExt;
extern FS_Archive ArchiveThemeExt;
Result open_archives(void); Result open_archives(void);
Result close_archives(void); Result close_archives(void);
@@ -41,11 +39,8 @@ Result close_archives(void);
u32 file_to_buf(FS_Path path, FS_Archive archive, char** buf); u32 file_to_buf(FS_Path path, FS_Archive archive, char** buf);
u32 zip_memory_to_buf(char *file_name, void * zip_memory, size_t zip_size, char ** buf); u32 zip_memory_to_buf(char *file_name, void * zip_memory, size_t zip_size, char ** buf);
u32 zip_file_to_buf(char *file_name, u16 *zip_path, char **buf); u32 zip_file_to_buf(char *file_name, u16 *zip_path, char **buf);
u32 decompress_lz_file(FS_Path file_name, FS_Archive archive, char **buf);
u32 compress_lz_file_fast(FS_Path path, FS_Archive archive, char *in_buf, u32 size);
Result buf_to_file(u32 size, FS_Path path, FS_Archive archive, char *buf); Result buf_to_file(u32 size, FS_Path path, FS_Archive archive, char *buf);
void remake_file(FS_Path path, FS_Archive archive, u32 size); void remake_file(FS_Path path, FS_Archive archive, u32 size);
void save_zip_to_sd(char * filename, u32 size, char * buf, EntryMode mode);
#endif #endif

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -128,11 +128,11 @@ Instructions_s extra_instructions[3] = {
}, },
{ {
"\uE07B Browse ThemePlaza", "\uE07B Browse ThemePlaza",
NULL NULL,
}, },
{ {
"\uE004 Sorting menu", "\uE004 Sorting menu",
"\uE005 Dumping menu" NULL
}, },
{ {
"Exit", "Exit",
@@ -140,27 +140,6 @@ Instructions_s extra_instructions[3] = {
} }
} }
}, },
{
.info_line = "Release \uE002 to cancel or hold \uE006 and release \uE002 to dump",
.instructions = {
{
"\uE079 Dump Current Theme",
"\uE07A Dump All Themes"
},
{
NULL,
NULL
},
{
NULL,
NULL
},
{
"Exit",
NULL
}
}
}
}; };
#endif #endif

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -127,4 +127,4 @@ void handle_scrolling(Entry_List_s * list);
void load_icons_thread(void * void_arg); void load_icons_thread(void * void_arg);
u32 load_data(char * filename, Entry_s entry, char ** buf); u32 load_data(char * filename, Entry_s entry, char ** buf);
#endif #endif

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -34,7 +34,7 @@
#include <tremor/ivorbisfile.h> #include <tremor/ivorbisfile.h>
#include <tremor/ivorbiscodec.h> #include <tremor/ivorbiscodec.h>
#define BUF_TO_READ 48000 // How much data should be buffered at a time #define BUF_TO_READ 40960 // How much data should be buffered at a time
typedef struct { typedef struct {
OggVorbis_File vf; OggVorbis_File vf;
@@ -51,4 +51,4 @@ typedef struct {
void play_audio(audio_s *); void play_audio(audio_s *);
#endif #endif

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -29,9 +29,8 @@
#include "common.h" #include "common.h"
#include "draw.h" #include "draw.h"
#include <ctype.h>
#define THEMEPLAZA_BASE_URL "http://themeplaza.art" #define THEMEPLAZA_BASE_URL "https://themeplaza.eu"
#define THEMEPLAZA_API_URL "/api/anemone/v1" #define THEMEPLAZA_API_URL "/api/anemone/v1"
#define THEMEPLAZA_BASE_API_URL THEMEPLAZA_BASE_URL THEMEPLAZA_API_URL #define THEMEPLAZA_BASE_API_URL THEMEPLAZA_BASE_URL THEMEPLAZA_API_URL
@@ -52,6 +51,6 @@
#define CACHE_PATH_FORMAT "/3ds/" APP_TITLE "/cache/%" JSON_INTEGER_FORMAT #define CACHE_PATH_FORMAT "/3ds/" APP_TITLE "/cache/%" JSON_INTEGER_FORMAT
bool themeplaza_browser(EntryMode mode); bool themeplaza_browser(EntryMode mode);
Result http_get(const char *url, char ** filename, char ** buf, u32 * size, InstallType install_type, const char * acceptable_mime_types); u32 http_get(const char *url, char ** filename, char ** buf, InstallType install_type);
#endif #endif

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -49,9 +49,8 @@ typedef struct {
u8 _padding1[0x13b8]; u8 _padding1[0x13b8];
ThemeEntry_s theme_entry; ThemeEntry_s theme_entry;
ThemeEntry_s shuffle_themes[MAX_SHUFFLE_THEMES]; ThemeEntry_s shuffle_themes[MAX_SHUFFLE_THEMES];
u8 shuffle_seedA[0xb]; u8 _padding2[0xb];
u8 shuffle; bool shuffle;
u8 shuffle_seedB[0xa];
} SaveData_dat_s; } SaveData_dat_s;
typedef struct { typedef struct {
@@ -76,9 +75,6 @@ Result bgm_install(Entry_s theme);
Result shuffle_install(Entry_List_s themes); Result shuffle_install(Entry_List_s themes);
Result dump_current_theme(void);
Result dump_all_themes(void);
void themes_check_installed(void * void_arg); void themes_check_installed(void * void_arg);
#endif #endif

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -36,63 +36,28 @@
#include <archive.h> #include <archive.h>
#include <archive_entry.h> #include <archive_entry.h>
static void start_read(qr_data *data) void exit_qr(qr_data *data)
{ {
LightLock_Lock(&data->mut); DEBUG("Exiting QR\n");
while(data->writer_waiting || data->writer_active) svcSignalEvent(data->cancel);
{ while(!data->finished)
CondVar_WaitTimeout(&data->cond, &data->mut, 1000000); svcSleepThread(1000000);
} svcCloseHandle(data->cancel);
data->capturing = false;
AtomicIncrement(&data->num_readers_active); free(data->camera_buffer);
LightLock_Unlock(&data->mut); free(data->tex);
} quirc_destroy(data->context);
static void stop_read(qr_data *data)
{
LightLock_Lock(&data->mut);
AtomicDecrement(&data->num_readers_active);
if(data->num_readers_active == 0)
{
CondVar_Signal(&data->cond);
}
LightLock_Unlock(&data->mut);
}
static void start_write(qr_data *data)
{
LightLock_Lock(&data->mut);
data->writer_waiting = true;
while(data->num_readers_active)
{
CondVar_WaitTimeout(&data->cond, &data->mut, 1000000);
}
data->writer_waiting = false;
data->writer_active = true;
LightLock_Unlock(&data->mut);
}
static void stop_write(qr_data *data)
{
LightLock_Lock(&data->mut);
data->writer_active = false;
CondVar_Broadcast(&data->cond);
LightLock_Unlock(&data->mut);
} }
static void capture_cam_thread(void *arg) void capture_cam_thread(void *arg)
{ {
qr_data *data = (qr_data *) arg; qr_data *data = (qr_data *) arg;
Handle events[3] = {0};
Handle cam_events[3] = {0}; events[0] = data->cancel;
cam_events[0] = data->event_stop;
u32 transferUnit; u32 transferUnit;
const u32 bufsz = 400 * 240 * sizeof(u16);
u16 *buffer = linearAlloc(bufsz);
u16 *buffer = calloc(1, 400 * 240 * sizeof(u16));
camInit(); camInit();
CAMU_SetSize(SELECT_OUT1, SIZE_CTR_TOP_LCD, CONTEXT_A); CAMU_SetSize(SELECT_OUT1, SIZE_CTR_TOP_LCD, CONTEXT_A);
CAMU_SetOutputFormat(SELECT_OUT1, OUTPUT_RGB_565, CONTEXT_A); CAMU_SetOutputFormat(SELECT_OUT1, OUTPUT_RGB_565, CONTEXT_A);
@@ -101,42 +66,38 @@ static void capture_cam_thread(void *arg)
CAMU_SetAutoExposure(SELECT_OUT1, true); CAMU_SetAutoExposure(SELECT_OUT1, true);
CAMU_SetAutoWhiteBalance(SELECT_OUT1, true); CAMU_SetAutoWhiteBalance(SELECT_OUT1, true);
CAMU_Activate(SELECT_OUT1); CAMU_Activate(SELECT_OUT1);
CAMU_GetBufferErrorInterruptEvent(&cam_events[2], PORT_CAM1); CAMU_GetBufferErrorInterruptEvent(&events[2], PORT_CAM1);
CAMU_SetTrimming(PORT_CAM1, false); CAMU_SetTrimming(PORT_CAM1, false);
CAMU_GetMaxBytes(&transferUnit, 400, 240); CAMU_GetMaxBytes(&transferUnit, 400, 240);
CAMU_SetTransferBytes(PORT_CAM1, transferUnit, 400, 240); CAMU_SetTransferBytes(PORT_CAM1, transferUnit, 400, 240);
CAMU_ClearBuffer(PORT_CAM1); CAMU_ClearBuffer(PORT_CAM1);
CAMU_SetReceiving(&cam_events[1], buffer, PORT_CAM1, bufsz, transferUnit); CAMU_SetReceiving(&events[1], buffer, PORT_CAM1, 400 * 240 * sizeof(u16), (s16) transferUnit);
CAMU_StartCapture(PORT_CAM1); CAMU_StartCapture(PORT_CAM1);
svcSignalEvent(data->started);
bool cancel = false; bool cancel = false;
while (!cancel)
while (!cancel)
{ {
s32 index = 0; s32 index = 0;
svcWaitSynchronizationN(&index, cam_events, 3, false, U64_MAX); svcWaitSynchronizationN(&index, events, 3, false, U64_MAX);
switch(index) { switch(index) {
case 0: case 0:
DEBUG("Cancel event received\n"); DEBUG("Cancel event received\n");
cancel = true; cancel = true;
break; break;
case 1: case 1:
svcCloseHandle(cam_events[1]); svcCloseHandle(events[1]);
cam_events[1] = 0; events[1] = 0;
svcWaitSynchronization(data->mutex, U64_MAX);
start_write(data); memcpy(data->camera_buffer, buffer, 400 * 240 * sizeof(u16));
memcpy(data->camera_buffer, buffer, bufsz); GSPGPU_FlushDataCache(data->camera_buffer, 400 * 240 * sizeof(u16));
data->any_update = true; svcReleaseMutex(data->mutex);
stop_write(data); CAMU_SetReceiving(&events[1], buffer, PORT_CAM1, 400 * 240 * sizeof(u16), transferUnit);
CAMU_SetReceiving(&cam_events[1], buffer, PORT_CAM1, bufsz, transferUnit);
break; break;
case 2: case 2:
svcCloseHandle(cam_events[1]); svcCloseHandle(events[1]);
cam_events[1] = 0; events[1] = 0;
CAMU_ClearBuffer(PORT_CAM1); CAMU_ClearBuffer(PORT_CAM1);
CAMU_SetReceiving(&cam_events[1], buffer, PORT_CAM1, bufsz, transferUnit); CAMU_SetReceiving(&events[1], buffer, PORT_CAM1, 400 * 240 * sizeof(u16), transferUnit);
CAMU_StartCapture(PORT_CAM1); CAMU_StartCapture(PORT_CAM1);
break; break;
default: default:
@@ -154,266 +115,213 @@ static void capture_cam_thread(void *arg)
CAMU_ClearBuffer(PORT_CAM1); CAMU_ClearBuffer(PORT_CAM1);
CAMU_Activate(SELECT_NONE); CAMU_Activate(SELECT_NONE);
camExit(); camExit();
free(buffer);
linearFree(buffer); for(int i = 0; i < 3; i++) {
for(int i = 1; i < 3; i++) if(events[i] != 0) {
{ svcCloseHandle(events[i]);
if(cam_events[i] != 0) { events[i] = 0;
svcCloseHandle(cam_events[i]);
cam_events[i] = 0;
} }
} }
svcCloseHandle(data->mutex);
LightEvent_Signal(&data->event_cam_info); data->finished = true;
} }
static void update_ui(void *arg) void update_ui(void *arg)
{ {
qr_data* data = (qr_data*) arg; qr_data* data = (qr_data*) arg;
C3D_Tex tex;
static const Tex3DS_SubTexture subt3x = { 400, 240, 0.0f, 1.0f, 400.0f/512.0f, 1.0f - (240.0f/256.0f) }; while (!data->finished)
C3D_TexInit(&tex, 512, 256, GPU_RGB565);
C3D_TexSetFilter(&tex, GPU_LINEAR, GPU_LINEAR);
while(svcWaitSynchronization(data->event_stop, 2 * 1000 * 1000ULL) == 0x09401BFE) // timeout of 2ms occured, still have 14 for copy and render
{ {
draw_base_interface(); draw_base_interface();
// Untiled texture loading code adapted from FBI // Untiled texture loading code adapted from FBI
start_read(data); svcWaitSynchronization(data->mutex, U64_MAX);
if(data->any_update) for(u32 x = 0; x < 400 && !data->finished; x++) {
{ for(u32 y = 0; y < 256 && !data->finished; y++) {
for(u32 y = 0; y < 240; y++) { u32 dstPos = ((((y >> 3) * (512 >> 3) + (x >> 3)) << 6) + ((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3))) * sizeof(u16);
const u32 srcPos = y * 400; u32 srcPos = (y * 400 + x) * sizeof(u16);
for(u32 x = 0; x < 400; x++) {
const u32 dstPos = ((((y >> 3) * (512 >> 3) + (x >> 3)) << 6) + ((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3)));
((u16*)tex.data)[dstPos] = data->camera_buffer[srcPos + x]; memcpy(&((u8*) data->image.tex->data)[dstPos], &((u8*) data->camera_buffer)[srcPos], sizeof(u16));
}
} }
data->any_update = false;
} }
stop_read(data);
C2D_DrawImageAt((C2D_Image){ &tex, &subt3x }, 0.0f, 0.0f, 0.4f, NULL, 1.0f, 1.0f); svcReleaseMutex(data->mutex);
if (data->finished)
{
end_frame();
}
C2D_DrawImageAt(data->image, 0.0f, 0.0f, 0.4f, NULL, 1.0f, 1.0f);
set_screen(bottom); set_screen(bottom);
draw_text_center(GFX_BOTTOM, 4, 0.5, 0.5, 0.5, colors[COLOR_WHITE], "Press \uE005 To Quit"); draw_text_center(GFX_BOTTOM, 4, 0.5, 0.5, 0.5, colors[COLOR_WHITE], "Press \uE005 To Quit");
end_frame(); end_frame();
} }
C3D_TexDelete(&tex);
LightEvent_Signal(&data->event_ui_info);
} }
static bool start_capture_cam(qr_data *data) bool start_capture_cam(qr_data *data)
{ {
if((data->cam_thread = threadCreate(capture_cam_thread, data, 0x10000, 0x1A, 1, false)) == NULL) data->mutex = 0;
{ data->cancel = 0;
throw_error("Capture cam thread creation failed\nPlease report this to the developers", ERROR_LEVEL_ERROR); svcCreateEvent(&data->cancel, RESET_STICKY);
LightEvent_Signal(&data->event_cam_info); svcCreateMutex(&data->mutex, false);
LightEvent_Signal(&data->event_ui_info); if(threadCreate(capture_cam_thread, data, 0x10000, 0x1A, 1, true) == NULL)
return false; return false;
} svcWaitSynchronization(data->started, U64_MAX);
if((data->ui_thread = threadCreate(update_ui, data, 0x10000, 0x1A, 1, false)) == NULL) if(threadCreate(update_ui, data, 0x10000, 0x1A, 1, true) == NULL)
{ {
LightEvent_Signal(&data->event_ui_info); exit_qr(data);
return false; return false;
} }
return true; return true;
} }
static bool update_qr(qr_data *data, struct quirc_data* scan_data) void update_qr(qr_data *data)
{ {
hidScanInput();
if (hidKeysDown() & (KEY_R | KEY_B | KEY_TOUCH)) {
exit_qr(data);
return;
}
if (!data->capturing) {
if(start_capture_cam(data))
data->capturing = true;
else {
exit_qr(data);
return;
}
}
if (data->finished) {
exit_qr(data);
return;
}
int w; int w;
int h; int h;
u8 *image = (u8*) quirc_begin(data->context, &w, &h); u8 *image = (u8*) quirc_begin(data->context, &w, &h);
for (ssize_t x = 0; x < w; x++) {
start_read(data); for (ssize_t y = 0; y < h; y++) {
for (int y = 0; y < h; y++) { u16 px = data->camera_buffer[y * 400 + x];
const int actual_y = y * w; image[y * w + x] = (u8)(((((px >> 11) & 0x1F) << 3) + (((px >> 5) & 0x3F) << 2) + ((px & 0x1F) << 3)) / 3);
for (int x = 0; x < w; x++) {
const int actual_off = actual_y + x;
const u16 px = data->camera_buffer[actual_off];
image[actual_off] = (u8)(((((px >> 11) & 0x1F) << 3) + (((px >> 5) & 0x3F) << 2) + ((px & 0x1F) << 3)) / 3);
} }
} }
stop_read(data);
quirc_end(data->context); quirc_end(data->context);
if(quirc_count(data->context) > 0) if(quirc_count(data->context) > 0)
{ {
struct quirc_code code; struct quirc_code code;
struct quirc_data scan_data;
quirc_extract(data->context, 0, &code); quirc_extract(data->context, 0, &code);
if (!quirc_decode(&code, scan_data)) if (!quirc_decode(&code, &scan_data))
{ {
return true; exit_qr(data);
}
}
return false; draw_install(INSTALL_DOWNLOAD);
} char * zip_buf = NULL;
char * filename = NULL;
u32 zip_size = http_get((char*)scan_data.payload, &filename, &zip_buf, INSTALL_DOWNLOAD);
static void start_qr(qr_data *data) if(zip_size != 0)
{
svcCreateEvent(&data->event_stop, RESET_STICKY);
LightEvent_Init(&data->event_cam_info, RESET_STICKY);
LightEvent_Init(&data->event_ui_info, RESET_STICKY);
LightLock_Init(&data->mut);
CondVar_Init(&data->cond);
data->cam_thread = NULL;
data->ui_thread = NULL;
data->any_update = false;
data->context = quirc_new();
quirc_resize(data->context, 400, 240);
data->camera_buffer = calloc(1, 400 * 240 * sizeof(u16));
}
static void exit_qr(qr_data *data)
{
svcSignalEvent(data->event_stop);
LightEvent_Wait(&data->event_ui_info);
LightEvent_Clear(&data->event_ui_info);
if(data->ui_thread != NULL)
{
threadJoin(data->ui_thread, U64_MAX);
threadFree(data->ui_thread);
data->ui_thread = NULL;
}
LightEvent_Wait(&data->event_cam_info);
LightEvent_Clear(&data->event_cam_info);
if(data->cam_thread != NULL)
{
threadJoin(data->cam_thread, U64_MAX);
threadFree(data->cam_thread);
data->cam_thread = NULL;
}
free(data->camera_buffer);
data->camera_buffer = NULL;
svcCloseHandle(data->event_stop);
data->event_stop = 0;
}
bool init_qr(void)
{
qr_data data;
memset(&data, 0, sizeof(data));
start_qr(&data);
struct quirc_data* scan_data = calloc(1, sizeof(struct quirc_data));
const bool ready = start_capture_cam(&data);
bool finished = !ready;
while(!finished)
{
hidScanInput();
if (hidKeysDown() & (KEY_R | KEY_B | KEY_TOUCH))
{
break;
}
finished = update_qr(&data, scan_data);
svcSleepThread(50 * 1000 * 1000ULL); // only scan every 50ms
}
exit_qr(&data);
bool success = false;
if(finished && ready)
{
draw_install(INSTALL_DOWNLOAD);
char * zip_buf = NULL;
char * filename = NULL;
u32 zip_size;
Result res = http_get((char*)scan_data->payload, &filename, &zip_buf, &zip_size, INSTALL_DOWNLOAD, "application/zip");
if (R_FAILED(res))
{
free(filename);
free(zip_buf);
return false;
}
else if (R_DESCRIPTION(res) == RD_NO_DATA || R_DESCRIPTION(res) == RD_CANCEL_REQUESTED)
{
free(filename);
return true;
}
if(zip_size != 0)
{
draw_install(INSTALL_CHECKING_DOWNLOAD);
struct archive *a = archive_read_new();
archive_read_support_format_zip(a);
int r = archive_read_open_memory(a, zip_buf, zip_size);
archive_read_free(a);
if(r == ARCHIVE_OK)
{ {
EntryMode mode = MODE_AMOUNT; draw_install(INSTALL_CHECKING_DOWNLOAD);
char * buf = NULL; struct archive *a = archive_read_new();
do { archive_read_support_format_zip(a);
if(zip_memory_to_buf("body_LZ.bin", zip_buf, zip_size, &buf) != 0)
{
mode = MODE_THEMES;
break;
}
free(buf); int r = archive_read_open_memory(a, zip_buf, zip_size);
buf = NULL; archive_read_free(a);
if(zip_memory_to_buf("splash.bin", zip_buf, zip_size, &buf) != 0)
{
mode = MODE_SPLASHES;
break;
}
free(buf); if(r == ARCHIVE_OK)
buf = NULL;
if(zip_memory_to_buf("splashbottom.bin", zip_buf, zip_size, &buf) != 0)
{
mode = MODE_SPLASHES;
break;
}
}
while(false);
free(buf);
buf = NULL;
if(mode != MODE_AMOUNT)
{ {
save_zip_to_sd(filename, zip_size, zip_buf, mode); EntryMode mode = MODE_AMOUNT;
success = true;
char * buf = NULL;
do {
if(zip_memory_to_buf("body_LZ.bin", zip_buf, zip_size, &buf) != 0)
{
mode = MODE_THEMES;
break;
}
free(buf);
buf = NULL;
if(zip_memory_to_buf("splash.bin", zip_buf, zip_size, &buf) != 0)
{
mode = MODE_SPLASHES;
break;
}
free(buf);
buf = NULL;
if(zip_memory_to_buf("splashbottom.bin", zip_buf, zip_size, &buf) != 0)
{
mode = MODE_SPLASHES;
break;
}
}
while(false);
free(buf);
buf = NULL;
if(mode != MODE_AMOUNT)
{
char path_to_file[0x107] = {0};
sprintf(path_to_file, "%s%s", main_paths[mode], filename);
char * extension = strrchr(path_to_file, '.');
if (extension == NULL || strcmp(extension, ".zip"))
strcat(path_to_file, ".zip");
remake_file(fsMakePath(PATH_ASCII, path_to_file), ArchiveSD, zip_size);
buf_to_file(zip_size, fsMakePath(PATH_ASCII, path_to_file), ArchiveSD, zip_buf);
data->success = true;
}
else
{
throw_error("Zip downloaded is neither\na splash nor a theme.", ERROR_LEVEL_WARNING);
}
} }
else else
{ {
throw_error("Zip downloaded is neither\na splash nor a theme.", ERROR_LEVEL_WARNING); throw_error("File downloaded isn't a zip.", ERROR_LEVEL_WARNING);
} }
} }
else else
{ {
throw_error("File downloaded isn't a zip.", ERROR_LEVEL_WARNING); throw_error("Download failed.", ERROR_LEVEL_WARNING);
} }
free(zip_buf);
}
else
{
throw_error("Download failed.", ERROR_LEVEL_WARNING);
}
free(filename); free(filename);
free(zip_buf);
}
} }
free(scan_data);
quirc_destroy(data.context);
return success;
} }
bool init_qr(void)
{
qr_data *data = calloc(1, sizeof(qr_data));
data->capturing = false;
data->finished = false;
svcCreateEvent(&data->started, RESET_STICKY);
data->context = quirc_new();
quirc_resize(data->context, 400, 240);
data->camera_buffer = calloc(1, 400 * 240 * sizeof(u16));
data->tex = (C3D_Tex*)malloc(sizeof(C3D_Tex));
static const Tex3DS_SubTexture subt3x = { 512, 256, 0.0f, 1.0f, 1.0f, 0.0f };
data->image = (C2D_Image){ data->tex, &subt3x };
C3D_TexInit(data->image.tex, 512, 256, GPU_RGB565);
C3D_TexSetFilter(data->image.tex, GPU_LINEAR, GPU_LINEAR);
while (!data->finished) update_qr(data);
bool success = data->success;
free(data);
return success;
}

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -125,8 +125,6 @@ void init_screens(void)
C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_SPLASHES], staticBuf, "Downloading splash list, please wait..."); C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_SPLASHES], staticBuf, "Downloading splash list, please wait...");
C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_PREVIEW], staticBuf, "Downloading preview, please wait..."); C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_PREVIEW], staticBuf, "Downloading preview, please wait...");
C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_BGM], staticBuf, "Downloading BGM, please wait..."); C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_BGM], staticBuf, "Downloading BGM, please wait...");
C2D_TextParse(&text[TEXT_INSTALL_DUMPING_THEME], staticBuf, "Dumping theme, please wait...");
C2D_TextParse(&text[TEXT_INSTALL_DUMPING_ALL_THEMES], staticBuf, "Dumping official themes, please wait...");
for(int i = 0; i < TEXT_AMOUNT; i++) for(int i = 0; i < TEXT_AMOUNT; i++)
C2D_TextOptimize(&text[i]); C2D_TextOptimize(&text[i]);
@@ -276,7 +274,7 @@ void draw_base_interface(void)
set_screen(top); set_screen(top);
} }
void throw_error(const char* error, ErrorLevel level) void throw_error(char* error, ErrorLevel level)
{ {
Text bottom_text = TEXT_AMOUNT; Text bottom_text = TEXT_AMOUNT;
Color text_color = COLOR_WHITE; Color text_color = COLOR_WHITE;
@@ -302,7 +300,7 @@ void throw_error(const char* error, ErrorLevel level)
draw_base_interface(); draw_base_interface();
draw_text_center(GFX_TOP, 100, 0.5f, 0.6f, 0.6f, colors[text_color], error); draw_text_center(GFX_TOP, 100, 0.5f, 0.6f, 0.6f, colors[text_color], error);
draw_c2d_text_center(GFX_TOP, 170, 0.5f, 0.6f, 0.6f, colors[COLOR_WHITE], &text[bottom_text]); draw_c2d_text_center(GFX_TOP, 150, 0.5f, 0.6f, 0.6f, colors[COLOR_WHITE], &text[bottom_text]);
end_frame(); end_frame();
if(kDown & KEY_A) break; if(kDown & KEY_A) break;
@@ -436,7 +434,7 @@ void draw_text_wrap(float x, float y, float z, float scaleX, float scaleY, Color
if((consumed = decode_utf8(&codepoint, (unsigned char*)text)) == -1) if((consumed = decode_utf8(&codepoint, (unsigned char*)text)) == -1)
break; break;
float character_width = scaleX * (fontGetCharWidthInfo(NULL, fontGlyphIndexFromCodePoint(NULL, codepoint))->charWidth); float character_width = scaleX * (fontGetCharWidthInfo(fontGlyphIndexFromCodePoint(codepoint))->charWidth);
if((current_width += character_width) > max_width) if((current_width += character_width) > max_width)
{ {
char* last_space = NULL; char* last_space = NULL;
@@ -554,18 +552,13 @@ void draw_grid_interface(Entry_List_s* list, Instructions_s instructions)
vertical_offset += 24; vertical_offset += 24;
horizontal_offset += 16; horizontal_offset += 16;
// theoretically impossible to have no icon when from the api
/*
if(!current_entry->placeholder_color) if(!current_entry->placeholder_color)
{ {
*/
C2D_Image * image = list->icons[i]; C2D_Image * image = list->icons[i];
C2D_DrawImageAt(*image, horizontal_offset, vertical_offset, 0.5f, NULL, 1.0f, 1.0f); C2D_DrawImageAt(*image, horizontal_offset, vertical_offset, 0.5f, NULL, 1.0f, 1.0f);
/*
} }
else else
C2D_DrawRectSolid(horizontal_offset, vertical_offset, 0.5f, list->entry_size, list->entry_size, current_entry->placeholder_color); C2D_DrawRectSolid(horizontal_offset, vertical_offset, 0.5f, list->entry_size, list->entry_size, current_entry->placeholder_color);
*/
if(i == selected_entry) if(i == selected_entry)
{ {

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -25,30 +25,25 @@
*/ */
#include <strings.h> #include <strings.h>
#include "fs.h" #include "fs.h"
#include "draw.h"
#include "unicode.h" #include "unicode.h"
#include <archive.h> #include <archive.h>
#include <archive_entry.h> #include <archive_entry.h>
FS_Archive ArchiveSD;
FS_Archive ArchiveHomeExt;
FS_Archive ArchiveThemeExt;
Result open_archives(void) Result open_archives(void)
{ {
romfsInit(); romfsInit();
u8 regionCode; u8 regionCode;
u32 archive1; u32 archive1;
u32 archive2; u32 archive2;
Result res = 0; Result res = 0;
FS_Path home; FS_Path home;
FS_Path theme; FS_Path theme;
CFGU_SecureInfoGetRegion(&regionCode); CFGU_SecureInfoGetRegion(&regionCode);
switch(regionCode) switch(regionCode)
{ {
@@ -68,7 +63,7 @@ Result open_archives(void)
archive1 = 0x00; archive1 = 0x00;
archive2 = 0x00; archive2 = 0x00;
} }
if(R_FAILED(res = FSUSER_OpenArchive(&ArchiveSD, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")))) return res; if(R_FAILED(res = FSUSER_OpenArchive(&ArchiveSD, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")))) return res;
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/Themes"), FS_ATTRIBUTE_DIRECTORY); FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/Themes"), FS_ATTRIBUTE_DIRECTORY);
@@ -76,43 +71,43 @@ Result open_archives(void)
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/3ds"), FS_ATTRIBUTE_DIRECTORY); FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/3ds"), FS_ATTRIBUTE_DIRECTORY);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/3ds/" APP_TITLE), FS_ATTRIBUTE_DIRECTORY); FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/3ds/" APP_TITLE), FS_ATTRIBUTE_DIRECTORY);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/3ds/" APP_TITLE "/cache"), FS_ATTRIBUTE_DIRECTORY); FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/3ds/" APP_TITLE "/cache"), FS_ATTRIBUTE_DIRECTORY);
u32 homeMenuPath[3] = {MEDIATYPE_SD, archive2, 0}; u32 homeMenuPath[3] = {MEDIATYPE_SD, archive2, 0};
home.type = PATH_BINARY; home.type = PATH_BINARY;
home.size = 0xC; home.size = 0xC;
home.data = homeMenuPath; home.data = homeMenuPath;
if(R_FAILED(res = FSUSER_OpenArchive(&ArchiveHomeExt, ARCHIVE_EXTDATA, home))) return res; if(R_FAILED(res = FSUSER_OpenArchive(&ArchiveHomeExt, ARCHIVE_EXTDATA, home))) return res;
u32 themePath[3] = {MEDIATYPE_SD, archive1, 0}; u32 themePath[3] = {MEDIATYPE_SD, archive1, 0};
theme.type = PATH_BINARY; theme.type = PATH_BINARY;
theme.size = 0xC; theme.size = 0xC;
theme.data = themePath; theme.data = themePath;
if(R_FAILED(res = FSUSER_OpenArchive(&ArchiveThemeExt, ARCHIVE_EXTDATA, theme))) return res; if(R_FAILED(res = FSUSER_OpenArchive(&ArchiveThemeExt, ARCHIVE_EXTDATA, theme))) return res;
Handle test_handle; Handle test_handle;
if(R_FAILED(res = FSUSER_OpenFile(&test_handle, ArchiveThemeExt, fsMakePath(PATH_ASCII, "/ThemeManage.bin"), FS_OPEN_READ, 0))) return res; if(R_FAILED(res = FSUSER_OpenFile(&test_handle, ArchiveThemeExt, fsMakePath(PATH_ASCII, "/ThemeManage.bin"), FS_OPEN_READ, 0))) return res;
FSFILE_Close(test_handle); FSFILE_Close(test_handle);
return 0; return 0;
} }
Result close_archives(void) Result close_archives(void)
{ {
Result res; Result res;
if(R_FAILED(res = FSUSER_CloseArchive(ArchiveSD))) return res; if(R_FAILED(res = FSUSER_CloseArchive(ArchiveSD))) return res;
if(R_FAILED(res = FSUSER_CloseArchive(ArchiveHomeExt))) return res; if(R_FAILED(res = FSUSER_CloseArchive(ArchiveHomeExt))) return res;
if(R_FAILED(res = FSUSER_CloseArchive(ArchiveThemeExt))) return res; if(R_FAILED(res = FSUSER_CloseArchive(ArchiveThemeExt))) return res;
return 0; return 0;
} }
u32 file_to_buf(FS_Path path, FS_Archive archive, char** buf) u32 file_to_buf(FS_Path path, FS_Archive archive, char** buf)
{ {
Handle file; Handle file;
Result res = 0; Result res = 0;
if (R_FAILED(res = FSUSER_OpenFile(&file, archive, path, FS_OPEN_READ, 0))) return 0; if (R_FAILED(res = FSUSER_OpenFile(&file, archive, path, FS_OPEN_READ, 0))) return 0;
u64 size; u64 size;
FSFILE_GetSize(file, &size); FSFILE_GetSize(file, &size);
if(size != 0) if(size != 0)
@@ -177,7 +172,6 @@ u32 zip_file_to_buf(char *file_name, u16 *zip_path, char **buf)
archive_read_support_format_zip(a); archive_read_support_format_zip(a);
int r = archive_read_open_filename(a, path, 0x4000); int r = archive_read_open_filename(a, path, 0x4000);
free(path);
if(r != ARCHIVE_OK) if(r != ARCHIVE_OK)
{ {
DEBUG("Invalid zip being opened\n"); DEBUG("Invalid zip being opened\n");
@@ -196,149 +190,7 @@ Result buf_to_file(u32 size, FS_Path path, FS_Archive archive, char *buf)
if (R_FAILED(res = FSFILE_Close(handle))) return res; if (R_FAILED(res = FSFILE_Close(handle))) return res;
return 0; return 0;
} }
u32 decompress_lz_file(FS_Path file_name, FS_Archive archive, char **buf)
{
Handle handle;
Result res = 0;
if (R_FAILED(res = FSUSER_OpenFile(&handle, archive, file_name, FS_OPEN_READ, 0))) {
DEBUG("%lu\n", res);
return 0;
}
u64 size;
FSFILE_GetSize(handle, &size);
char *temp_buf = NULL;
if(size != 0)
{
temp_buf = calloc(1, size);
FSFILE_Read(handle, NULL, 0, temp_buf, size);
}
FSFILE_Close(handle);
if (temp_buf[0] != 0x11) {
free(temp_buf);
return 0;
}
u32 output_size = temp_buf[1] | ((temp_buf[2] << 8) & 0xFF00) | ((temp_buf[3] << 16) & 0xFF0000);
printf("%ld\n", output_size);
*buf = calloc(1, output_size);
u32 pos = 4;
u32 cur_written = 0;
u8 counter = 0;
u8 mask = 0;
while (cur_written < output_size)
{
if (counter == 0) // read mask
{
mask = temp_buf[pos++];
counter++;
continue;
}
if ((mask >> (8 - counter)) & 0x01) // compressed block
{
int len = 0;
int disp = 0;
switch (temp_buf[pos] >> 4)
{
case 0:
len = temp_buf[pos++] << 4;
len |= temp_buf[pos] >> 4;
len += 0x11;
break;
case 1:
len = (temp_buf[pos++] & 0x0F) << 12;
len |= temp_buf[pos++] << 4;
len |= temp_buf[pos] >> 4;
len += 0x111;
break;
default:
len = (temp_buf[pos] >> 4) + 1;
}
disp = (temp_buf[pos++] & 0x0F) << 8;
disp |= temp_buf[pos++];
for (int i = 0; i < len; ++i)
{
*(*buf + cur_written + i) = *(*buf + cur_written - disp - 1 + i);
}
cur_written += len;
}
else // byte literal
{
*(*buf + cur_written) = temp_buf[pos++];
cur_written++;
}
if (++counter > 8) counter = 0;
}
free(temp_buf);
return cur_written;
}
// This is an awful algorithm to "compress" LZ11 data.
// We do this instead of actual LZ11 compression because of time -
// LZ11 requires a lot of mem searching which is painfully slow on 3DS.
// This process is nearly instant but means the resulting file is actually larger
// than the input file. I don't think this is a problem as the only file we compress like this
// is the theme data installed to extdata in some rare cases, which means only 1 file at most
// is ever compressed like this. I don't think 400 KB is that big a sacrifice for probably
// half a minute or so of time save - I may change my mind on this in the future, especially
// if i figure out a dynamic programming algorithm which ends up being significantly
// faster. Otherwise, I think this is probably a fine implementation.
u32 compress_lz_file_fast(FS_Path path, FS_Archive archive, char *in_buf, u32 size)
{
char *output_buf = calloc(1, size * 2);
u32 output_size = 0;
u32 mask_pos = 0;
u32 bytes_processed = 0;
u8 counter = 0;
if (output_buf == NULL) return 0;
// Set header data for the LZ11 file - 0x11 is version (LZ11), next 3 bytes are size
output_buf[0] = 0x11;
output_buf[3] = (size & 0xFF0000) >> 16;
output_buf[2] = (size & 0xFF00) >> 8;
output_buf[1] = (size & 0xFF);
output_size += 4;
while (bytes_processed < size)
{
if (counter == 0)
{
mask_pos = output_size++;
output_buf[mask_pos] = 0;
}
output_buf[output_size++] = in_buf[bytes_processed++];
if (++counter == 8) counter = 0;
}
buf_to_file(output_size, path, archive, output_buf);
free(output_buf);
return output_size;
}
void remake_file(FS_Path path, FS_Archive archive, u32 size) void remake_file(FS_Path path, FS_Archive archive, u32 size)
{ {
Handle handle; Handle handle;
@@ -351,116 +203,4 @@ void remake_file(FS_Path path, FS_Archive archive, u32 size)
char *buf = calloc(size, 1); char *buf = calloc(size, 1);
buf_to_file(size, path, archive, buf); buf_to_file(size, path, archive, buf);
free(buf); free(buf);
} }
static SwkbdCallbackResult fat32filter(void *user, const char **ppMessage, const char *text, size_t textlen)
{
(void)textlen;
(void)user;
*ppMessage = "Input must not contain:\n" ILLEGAL_CHARS;
if(strpbrk(text, ILLEGAL_CHARS))
{
DEBUG("illegal filename: %s\n", text);
return SWKBD_CALLBACK_CONTINUE;
}
return SWKBD_CALLBACK_OK;
}
// assumes the input buffer is a ZIP. if it isn't, why are you calling this?
void save_zip_to_sd(char * filename, u32 size, char * buf, EntryMode mode)
{
static char path_to_file[32761]; // FAT32 paths can be quite long.
const int max_chars = 250;
char new_filename[max_chars + 5]; // .zip + \0
renamed:
sprintf(path_to_file, "%s%s", main_paths[mode], filename);
// filter out characters illegal in FAT32 filenames
char * curr_filename = strrchr(path_to_file, '/') + 1;
char * illegal_char = curr_filename;
while ((illegal_char = strpbrk(illegal_char, ILLEGAL_CHARS)))
{
DEBUG("Illegal char found in filename: %c\n", *illegal_char);
if (*illegal_char == '.')
{
// skip initial . (this is allowed)
if (illegal_char == curr_filename)
continue;
// skip extension delimiter
if (strpbrk(illegal_char + 1, ".") == NULL)
{
illegal_char++;
continue;
}
}
*illegal_char = '-';
}
// ensure the extension is .zip
char * extension = strrchr(path_to_file, '.');
if (extension == NULL || strcmp(extension, ".zip"))
strcat(path_to_file, ".zip");
DEBUG("path: %s\n", path_to_file);
FS_Path path = fsMakePath(PATH_ASCII, path_to_file);
// check if file already exists, and if it does, prompt the user
// to overwrite or change name (or exit)
Result res = FSUSER_CreateFile(ArchiveSD, path, 0, size);
if (R_FAILED(res))
{
if (res == (long)0xC82044BE)
{
DEBUG("File already exists\n");
SwkbdState swkbd;
swkbdInit(&swkbd, SWKBD_TYPE_NORMAL, 3, max_chars / 2);
swkbdSetHintText(&swkbd, "Choose a new filename or tap Overwrite");
swkbdSetFeatures(&swkbd, SWKBD_PREDICTIVE_INPUT | SWKBD_DARKEN_TOP_SCREEN);
swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, "Cancel", false);
swkbdSetButton(&swkbd, SWKBD_BUTTON_MIDDLE, "Overwrite", false);
swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, "Rename", true);
swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, SWKBD_FILTER_CALLBACK, -1);
swkbdSetFilterCallback(&swkbd, &fat32filter, NULL);
SwkbdButton button = swkbdInputText(&swkbd, new_filename, max_chars);
switch (button)
{
case SWKBD_BUTTON_RIGHT:
DEBUG("Renaming to %s\n", new_filename);
strcat(new_filename, ".zip");
filename = new_filename;
goto renamed;
case SWKBD_BUTTON_MIDDLE:
// we good
DEBUG("Overwriting %s\n", filename);
break;
case SWKBD_BUTTON_LEFT:
// do nothing
DEBUG("File rename cancelled\n");
return;
case SWKBD_BUTTON_NONE:
DEBUG("SWKBD broke wtf??? :- %x\n", swkbdGetResult(&swkbd));
return throw_error("???\nTry a USB keyboard", ERROR_LEVEL_WARNING);
}
}
else if (res == (long)0xC86044D2)
{
DEBUG("SD card is full\n");
return throw_error("SD card is full.\nDelete some themes to make space.", ERROR_LEVEL_WARNING);
}
else
{
DEBUG("error: %lx\n", res);
return throw_error("FS Error:\nGet a new SD card.", ERROR_LEVEL_ERROR);
}
}
DEBUG("Saving to SD: %s\n", path_to_file);
remake_file(path, ArchiveSD, size);
buf_to_file(size, path, ArchiveSD, buf);
}

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -83,7 +83,6 @@ C2D_Image * loadTextureIcon(Icon_s *icon)
void parse_smdh(Icon_s *icon, Entry_s * entry, const u16 * fallback_name) void parse_smdh(Icon_s *icon, Entry_s * entry, const u16 * fallback_name)
{ {
if(icon == NULL) if(icon == NULL)
{ {
memcpy(entry->name, fallback_name, 0x80); memcpy(entry->name, fallback_name, 0x80);
@@ -93,12 +92,27 @@ void parse_smdh(Icon_s *icon, Entry_s * entry, const u16 * fallback_name)
return; return;
} }
memcpy(entry->name, icon->name, 0x40*sizeof(u16)); memcpy(entry->name, icon->name, 0x40*sizeof(u16));
memcpy(entry->desc, icon->desc, 0x80*sizeof(u16)); memcpy(entry->desc, icon->desc, 0x80*sizeof(u16));
memcpy(entry->author, icon->author, 0x40*sizeof(u16)); memcpy(entry->author, icon->author, 0x40*sizeof(u16));
} }
static void parse_entry_smdh(Entry_s * entry, const u16 * fallback_name)
{
char *info_buffer = NULL;
u64 size = load_data("/info.smdh", *entry, &info_buffer);
if(!size)
{
free(info_buffer);
info_buffer = NULL;
}
Icon_s * smdh = (Icon_s *)info_buffer;
parse_smdh(smdh, entry, fallback_name);
}
static C2D_Image * load_entry_icon(Entry_s entry) static C2D_Image * load_entry_icon(Entry_s entry)
{ {
char *info_buffer = NULL; char *info_buffer = NULL;
@@ -106,9 +120,7 @@ static C2D_Image * load_entry_icon(Entry_s entry)
if(!size) return NULL; if(!size) return NULL;
Icon_s * smdh = (Icon_s *)info_buffer; Icon_s * smdh = (Icon_s *)info_buffer;
C2D_Image* out = loadTextureIcon(smdh); return loadTextureIcon(smdh);
free(info_buffer);
return out;
} }
typedef int (*sort_comparator)(const void *, const void *); typedef int (*sort_comparator)(const void *, const void *);
@@ -175,34 +187,17 @@ Result load_entries(const char * loading_path, Entry_List_s * list)
if(R_FAILED(res) || entries_read == 0) if(R_FAILED(res) || entries_read == 0)
break; break;
if(!(dir_entry.attributes & FS_ATTRIBUTE_DIRECTORY) && strcmp(dir_entry.shortExt, "ZIP")) if(!(dir_entry.attributes & FS_ATTRIBUTE_DIRECTORY) && strcmp(dir_entry.shortExt, "ZIP"))
continue; continue;
u16 path[0x106] = {0};
struacat(path, loading_path);
strucat(path, dir_entry.name);
char * buf = NULL;
if (!strcmp(dir_entry.shortExt, "ZIP"))
{
zip_file_to_buf("info.smdh", path, &buf);
}
else
{
const ssize_t len = strulen(path, 0x106);
struacat(path, "/info.smdh");
file_to_buf(fsMakePath(PATH_UTF16, path), ArchiveSD, &buf);
memset(&path[len], 0, (0x106 - len) * sizeof(u16));
}
list->entries_count++; list->entries_count++;
Entry_s * new_list = realloc(list->entries, list->entries_count * sizeof(Entry_s)); Entry_s * new_list = realloc(list->entries, list->entries_count * sizeof(Entry_s));
if(new_list == NULL) if(new_list == NULL)
{ {
// out of memory: still allow use of currently loaded entries. free(list->entries);
// Many things might die, depending on the heap layout after list->entries = NULL;
list->entries_count--; res = -1;
free(buf); DEBUG("break\n");
break; break;
} }
else else
@@ -210,11 +205,12 @@ Result load_entries(const char * loading_path, Entry_List_s * list)
Entry_s * current_entry = &(list->entries[list->entries_count-1]); Entry_s * current_entry = &(list->entries[list->entries_count-1]);
memset(current_entry, 0, sizeof(Entry_s)); memset(current_entry, 0, sizeof(Entry_s));
parse_smdh((Icon_s *)buf, current_entry, dir_entry.name);
free(buf);
memcpy(current_entry->path, path, 0x106 * sizeof(u16)); struacat(current_entry->path, loading_path);
strucat(current_entry->path, dir_entry.name);
current_entry->is_zip = !strcmp(dir_entry.shortExt, "ZIP"); current_entry->is_zip = !strcmp(dir_entry.shortExt, "ZIP");
parse_entry_smdh(current_entry, dir_entry.name);
} }
FSDIR_Close(dir_handle); FSDIR_Close(dir_handle);
@@ -349,18 +345,19 @@ static bool load_icons(Entry_List_s * current_list, Handle mutex)
if(abs(delta) >= current_list->entries_count - current_list->entries_loaded*(ICONS_OFFSET_AMOUNT-1)) if(abs(delta) >= current_list->entries_count - current_list->entries_loaded*(ICONS_OFFSET_AMOUNT-1))
delta = -SIGN(delta) * (current_list->entries_count - abs(delta)); delta = -SIGN(delta) * (current_list->entries_count - abs(delta));
int absdelta = abs(delta);
int starti = current_list->scroll; int starti = current_list->scroll;
int endi = starti + abs(delta); int endi = starti + absdelta;
if(delta < 0) if(delta < 0)
{ {
endi -= abs(delta) + 1; endi -= absdelta + 1;
starti += abs(delta) - 1; starti += absdelta - 1;
} }
int ctr = 0; int ctr = 0;
Entry_s ** entries = calloc(abs(delta), sizeof(Entry_s *)); Entry_s ** entries = calloc(absdelta, sizeof(Entry_s *));
int * indexes = calloc(abs(delta), sizeof(int)); int * indexes = calloc(absdelta, sizeof(int));
bool released = false; bool released = false;
C2D_Image ** icons = current_list->icons; C2D_Image ** icons = current_list->icons;
@@ -393,36 +390,54 @@ static bool load_icons(Entry_List_s * current_list, Handle mutex)
indexes[ctr] = index; indexes[ctr] = index;
} }
#undef SIGN static int cutoff[] = {
0, // absdelta = 1
if(abs(delta) < 4) 0, // absdelta = 2
0, // absdelta = 3
0, // absdelta = 4
1, // absdelta = 5, 1 visible to be replaced before allowing drawing
2, // absdelta = 6, 2 visible to be replaced before allowing drawing
3, // absdelta = 7, 3 visible to be replaced before allowing drawing
};
int actual_cutoff = cutoff[absdelta-1];
if(actual_cutoff == 0)
{ {
svcReleaseMutex(mutex); svcReleaseMutex(mutex);
released = true; released = true;
svcSleepThread(1e7);
} }
svcSleepThread(1e7); if(delta > 0)
starti = 0; {
endi = abs(delta); starti = 0;
for(int i = starti; i < endi; i++) endi = absdelta;
}
else
{
starti = absdelta-1;
endi = -1;
}
int change = SIGN(delta);
#undef SIGN
for(int i = starti; i != endi; i += change)
{ {
Entry_s * current_entry = entries[i]; Entry_s * current_entry = entries[i];
int index = indexes[i]; int index = indexes[i];
C2D_Image * image = icons[index]; C2D_Image * image = icons[index];
if (icons[index] != NULL) C3D_TexDelete(image->tex);
{ free(image->tex);
C3D_TexDelete(image->tex); free(image);
free(image->tex);
free(image);
}
icons[index] = load_entry_icon(*current_entry); icons[index] = load_entry_icon(*current_entry);
if(!released && i > endi/2) if(!released && i == actual_cutoff)
{ {
svcReleaseMutex(mutex); svcReleaseMutex(mutex);
released = true; released = true;
svcSleepThread(1e7);
} }
} }
@@ -498,7 +513,7 @@ bool load_preview_from_buffer(void * buf, u32 size, C2D_Image * preview_image, i
if(color_type == PNG_COLOR_TYPE_RGB || if(color_type == PNG_COLOR_TYPE_RGB ||
color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_PALETTE) color_type == PNG_COLOR_TYPE_PALETTE)
png_set_add_alpha(png, 0xFF, PNG_FILLER_BEFORE); png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
if(color_type == PNG_COLOR_TYPE_GRAY || if(color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA) color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
@@ -547,12 +562,8 @@ bool load_preview_from_buffer(void * buf, u32 size, C2D_Image * preview_image, i
memcpy(preview_image->tex->data + dst, px, sizeof(u32)); memcpy(preview_image->tex->data + dst, px, sizeof(u32));
} }
free(row_pointers[j]);
} }
free(row_pointers);
*preview_offset = (width-400)/2; *preview_offset = (width-400)/2;
return true; return true;
@@ -652,4 +663,4 @@ Result load_audio(Entry_s entry, audio_s *audio)
DEBUG("<load_audio> fmemopen failed!\n"); DEBUG("<load_audio> fmemopen failed!\n");
return MAKERESULT(RL_FATAL, RS_NOTFOUND, RM_APPLICATION, RD_NOT_FOUND); return MAKERESULT(RL_FATAL, RS_NOTFOUND, RM_APPLICATION, RD_NOT_FOUND);
} }
} }

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -132,7 +132,6 @@ static void free_icons(Entry_List_s * list)
for(int i = 0; i < amount; i++) for(int i = 0; i < amount; i++)
{ {
if (list->icons[i] == NULL) continue;
C3D_TexDelete(list->icons[i]->tex); C3D_TexDelete(list->icons[i]->tex);
free(list->icons[i]->tex); free(list->icons[i]->tex);
free(list->icons[i]); free(list->icons[i]);
@@ -240,7 +239,6 @@ static void load_lists(Entry_List_s * lists)
static SwkbdCallbackResult jump_menu_callback(void* entries_count, const char** ppMessage, const char* text, size_t textlen) static SwkbdCallbackResult jump_menu_callback(void* entries_count, const char** ppMessage, const char* text, size_t textlen)
{ {
(void)textlen;
int typed_value = atoi(text); int typed_value = atoi(text);
if(typed_value > *(int*)entries_count) if(typed_value > *(int*)entries_count)
{ {
@@ -318,7 +316,7 @@ static void toggle_shuffle(Entry_List_s * list)
current_entry->no_bgm_shuffle = true; current_entry->no_bgm_shuffle = true;
} }
} }
else else
{ {
current_entry->in_shuffle = true; current_entry->in_shuffle = true;
list->shuffle_count++; list->shuffle_count++;
@@ -360,6 +358,7 @@ int main(void)
bool preview_mode = false; bool preview_mode = false;
int preview_offset = 0; int preview_offset = 0;
bool qr_mode = false;
bool install_mode = false; bool install_mode = false;
bool extra_mode = false; bool extra_mode = false;
C2D_Image preview = {0}; C2D_Image preview = {0};
@@ -401,13 +400,14 @@ int main(void)
{ {
if(key_l) if(key_l)
index = 0; index = 0;
else if(key_r) // uncomment when we use the right menu. we don't for now // else if(key_r) // uncomment when we use the right menu. we don't for now
index = 2; // index = 2;
} }
instructions = extra_instructions[index]; instructions = extra_instructions[index];
} }
if(preview_mode) if(qr_mode) take_picture();
else if(preview_mode)
{ {
draw_preview(preview, preview_offset); draw_preview(preview, preview_offset);
} }
@@ -439,14 +439,14 @@ int main(void)
if(!install_mode && !extra_mode) if(!install_mode && !extra_mode)
{ {
if(!preview_mode && kDown & KEY_L) //toggle between splashes and themes if(!preview_mode && !qr_mode && kDown & KEY_L) //toggle between splashes and themes
{ {
switch_mode: switch_mode:
current_mode++; current_mode++;
current_mode %= MODE_AMOUNT; current_mode %= MODE_AMOUNT;
continue; continue;
} }
else if(!preview_mode && kDown & KEY_R) //toggle QR mode else if(!qr_mode && !preview_mode && kDown & KEY_R) //toggle QR mode
{ {
enable_qr: enable_qr:
draw_base_interface(); draw_base_interface();
@@ -479,7 +479,7 @@ int main(void)
continue; continue;
} }
else if(kDown & KEY_Y && current_list->entries != NULL) //toggle preview mode else if(!qr_mode && kDown & KEY_Y && current_list->entries != NULL) //toggle preview mode
{ {
toggle_preview: toggle_preview:
if(!preview_mode) if(!preview_mode)
@@ -524,7 +524,7 @@ int main(void)
} }
} }
if(preview_mode || current_list->entries == NULL) if(qr_mode || preview_mode || current_list->entries == NULL)
goto touch; goto touch;
int selected_entry = current_list->selected_entry; int selected_entry = current_list->selected_entry;
@@ -670,23 +670,6 @@ int main(void)
load_icons_first(current_list, false); load_icons_first(current_list, false);
} }
} }
else if(key_r)
{
if(((kDown | kHeld)) & KEY_DUP)
{
draw_install(INSTALL_DUMPING_THEME);
Result res = dump_current_theme();
if (R_FAILED(res)) DEBUG("Dump theme result: %lx\n", res);
else load_lists(lists);
}
else if(((kDown | kHeld)) & KEY_DDOWN)
{
draw_install(INSTALL_DUMPING_ALL_THEMES);
Result res = dump_all_themes();
if (R_FAILED(res)) DEBUG("Dump all themes result: %lx\n", res);
else load_lists(lists);
}
}
} }
continue; continue;
} }

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View File

@@ -576,11 +576,10 @@ static quirc_decode_error_t codestream_ecc(struct quirc_data *data,
&quirc_version_db[data->version]; &quirc_version_db[data->version];
const struct quirc_rs_params *sb_ecc = &ver->ecc[data->ecc_level]; const struct quirc_rs_params *sb_ecc = &ver->ecc[data->ecc_level];
struct quirc_rs_params lb_ecc; struct quirc_rs_params lb_ecc;
const int lb_count = int bc = ver->data_bytes / sb_ecc->bs;
(ver->data_bytes - sb_ecc->bs * sb_ecc->ns) / (sb_ecc->bs + 1);
const int bc = lb_count + sb_ecc->ns;
const int ecc_offset = sb_ecc->dw * bc + lb_count;
int dst_offset = 0; int dst_offset = 0;
int lb_count = ver->data_bytes - bc * sb_ecc->bs;
int small_dw_total = bc * sb_ecc->dw;
int i; int i;
memcpy(&lb_ecc, sb_ecc, sizeof(lb_ecc)); memcpy(&lb_ecc, sb_ecc, sizeof(lb_ecc));
@@ -589,16 +588,22 @@ static quirc_decode_error_t codestream_ecc(struct quirc_data *data,
for (i = 0; i < bc; i++) { for (i = 0; i < bc; i++) {
uint8_t *dst = ds->data + dst_offset; uint8_t *dst = ds->data + dst_offset;
const struct quirc_rs_params *ecc = const struct quirc_rs_params *ecc = sb_ecc;
(i < sb_ecc->ns) ? sb_ecc : &lb_ecc;
const int num_ec = ecc->bs - ecc->dw;
quirc_decode_error_t err; quirc_decode_error_t err;
int j; int j = 0;
int k;
for (j = 0; j < ecc->dw; j++) for (k = 0; k < sb_ecc->dw; k++)
dst[j] = ds->raw[j * bc + i]; dst[j++] = ds->raw[k * bc + i];
for (j = 0; j < num_ec; j++)
dst[ecc->dw + j] = ds->raw[ecc_offset + j * 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); err = correct_block(dst, ecc);
if (err) if (err)
@@ -724,10 +729,10 @@ static quirc_decode_error_t decode_alpha(struct quirc_data *data,
int bits = 13; int bits = 13;
int count; int count;
if (data->version < 10) if (data->version < 7)
bits = 9; bits = 9;
else if (data->version < 27) else if (data->version < 11)
bits = 11; bits = 10;
count = take_bits(ds, bits); count = take_bits(ds, bits);
if (data->payload_len + count + 1 > QUIRC_MAX_PAYLOAD) if (data->payload_len + count + 1 > QUIRC_MAX_PAYLOAD)
@@ -790,18 +795,12 @@ static quirc_decode_error_t decode_kanji(struct quirc_data *data,
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
int d = take_bits(ds, 13); int d = take_bits(ds, 13);
int msB = d / 0xc0;
int lsB = d % 0xc0;
int intermediate = (msB << 8) | lsB;
uint16_t sjw; uint16_t sjw;
if (intermediate + 0x8140 <= 0x9ffc) { if (d + 0x8140 >= 0x9ffc)
/* bytes are in the range 0x8140 to 0x9FFC */ sjw = d + 0x8140;
sjw = intermediate + 0x8140; else
} else { sjw = d + 0xc140;
/* bytes are in the range 0xE040 to 0xEBBF */
sjw = intermediate + 0xc140;
}
data->payload[data->payload_len++] = sjw >> 8; data->payload[data->payload_len++] = sjw >> 8;
data->payload[data->payload_len++] = sjw & 0xff; data->payload[data->payload_len++] = sjw & 0xff;
@@ -874,7 +873,7 @@ static quirc_decode_error_t decode_payload(struct quirc_data *data,
done: done:
/* Add nul terminator to all payloads */ /* Add nul terminator to all payloads */
if (data->payload_len >= (int) sizeof(data->payload)) if ((unsigned int)data->payload_len >= sizeof(data->payload))
data->payload_len--; data->payload_len--;
data->payload[data->payload_len] = 0; data->payload[data->payload_len] = 0;

View File

@@ -14,7 +14,6 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
#include <limits.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <math.h> #include <math.h>
@@ -99,8 +98,8 @@ static void perspective_map(const double *c,
double x = (c[0]*u + c[1]*v + c[2]) / den; double x = (c[0]*u + c[1]*v + c[2]) / den;
double y = (c[3]*u + c[4]*v + c[5]) / den; double y = (c[3]*u + c[4]*v + c[5]) / den;
ret->x = (int) rint(x); ret->x = rint(x);
ret->y = (int) rint(y); ret->y = rint(y);
} }
static void perspective_unmap(const double *c, static void perspective_unmap(const double *c,
@@ -122,113 +121,234 @@ static void perspective_unmap(const double *c,
* Span-based floodfill routine * Span-based floodfill routine
*/ */
#define FLOOD_FILL_MAX_DEPTH 128
typedef void (*span_func_t)(void *user_data, int y, int left, int right); typedef void (*span_func_t)(void *user_data, int y, int left, int right);
static void flood_fill_seed(struct quirc *q, int x, int y, int from, int to, #if 0 // recursive flood fill
span_func_t func, void *user_data,
int depth) #define FLOOD_FILL_MAX_DEPTH 4096
struct flood_fill_params{
struct quirc *q;
int from;
int to;
span_func_t func;
void *user_data;
};
static struct flood_fill_params ffp;
static void flood_fill_rec(int x, int y, int depth)
{ {
int left = x; int left = x;
int right = x; int right = x;
int i; int i;
quirc_pixel_t *row = q->pixels + y * q->w; quirc_pixel_t *row = ffp.q->pixels + y * ffp.q->w;
if (depth >= FLOOD_FILL_MAX_DEPTH) if (!depth)
return; return;
while (left > 0 && row[left - 1] == from) while (left > 0 && row[left - 1] == ffp.from)
left--; left--;
while (right < q->w - 1 && row[right + 1] == from) while (right < ffp.q->w - 1 && row[right + 1] == ffp.from)
right++; right++;
/* Fill the extent */ /* Fill the extent */
for (i = left; i <= right; i++) for (i = left; i <= right; i++)
row[i] = to; row[i] = ffp.to;
if (func) if (ffp.func)
func(user_data, y, left, right); ffp.func(ffp.user_data, y, left, right);
/* Seed new flood-fills */ /* Seed new flood-fills */
if (y > 0) { if (y > 0) {
row = q->pixels + (y - 1) * q->w; row = ffp.q->pixels + (y - 1) * ffp.q->w;
for (i = left; i <= right; i++) for (i = left; i <= right; i++)
if (row[i] == from) if (row[i] == ffp.from)
flood_fill_seed(q, i, y - 1, from, to, flood_fill_rec(i, y - 1, depth - 1);
func, user_data, depth + 1);
} }
if (y < q->h - 1) { if (y < ffp.q->h - 1) {
row = q->pixels + (y + 1) * q->w; row = ffp.q->pixels + (y + 1) * ffp.q->w;
for (i = left; i <= right; i++) for (i = left; i <= right; i++)
if (row[i] == from) if (row[i] == ffp.from)
flood_fill_seed(q, i, y + 1, from, to, flood_fill_rec(i, y + 1, depth - 1);
func, user_data, depth + 1);
} }
} }
static void flood_fill_seed(struct quirc *q, int x, int y, int from, int to,
span_func_t func, void *user_data)
{
ffp.q = q;
ffp.from = from;
ffp.to = to;
ffp.func = func;
ffp.user_data = user_data;
flood_fill_rec(x, y, FLOOD_FILL_MAX_DEPTH);
}
#else // stacked flood fill
#define FILL_STACK_CHUNK_SIZE 0x400
struct fill_stack_chunk{
int x[FILL_STACK_CHUNK_SIZE];
int y[FILL_STACK_CHUNK_SIZE];
struct fill_stack_chunk *prev;
};
struct fill_stack{
struct fill_stack_chunk *last_chunk;
int index;
};
static void fill_stack_init(struct fill_stack *s){
s->last_chunk = NULL;
}
static int fill_stack_is_empty(struct fill_stack *s){
return s->last_chunk == NULL;
}
static void fill_stack_push(struct fill_stack *s, int x, int y){
struct fill_stack_chunk *c;
if(s->last_chunk != NULL && s->index < FILL_STACK_CHUNK_SIZE - 1){
c = s->last_chunk;
s->index++;
}else{
c = (struct fill_stack_chunk*)malloc(sizeof(struct fill_stack_chunk));
if(c == NULL){
return;
}
c->prev = s->last_chunk;
s->last_chunk = c;
s->index = 0;
}
c->x[s->index] = x;
c->y[s->index] = y;
}
static void fill_stack_pop(struct fill_stack *s, int *px, int *py){
struct fill_stack_chunk *c = s->last_chunk;
if(c == NULL){
return;
}
*px = c->x[s->index];
*py = c->y[s->index];
if(s->index > 0){
s->index--;
}else{
s->last_chunk = c->prev;
s->index = FILL_STACK_CHUNK_SIZE - 1;
free(c);
}
}
static void flood_fill_seed(struct quirc *q, int start_x, int start_y, int from, int to,
span_func_t func, void *user_data)
{
struct fill_stack s;
fill_stack_init(&s);
fill_stack_push(&s, start_x, start_y);
do{
int x = 0, y = 0;
fill_stack_pop(&s, &x, &y);
int left = x, right = x, i;
quirc_pixel_t *row = q->pixels + y * q->w;
while (left > 0 && row[left - 1] == from)
left--;
while (right < q->w - 1 && row[right + 1] == from)
right++;
/* Fill the extent */
for (i = left; i <= right; i++)
row[i] = to;
if (func)
func(user_data, y, left, right);
/* Seed new flood-fills */
if (y > 0) {
row = q->pixels + (y - 1) * q->w;
for (i = left; i <= right; i++)
if (row[i] == from)
fill_stack_push(&s, i, y - 1);
}
if (y < q->h - 1) {
row = q->pixels + (y + 1) * q->w;
for (i = left; i <= right; i++)
if (row[i] == from)
fill_stack_push(&s, i, y + 1);
}
}while(!fill_stack_is_empty(&s));
}
#endif
/************************************************************************ /************************************************************************
* Adaptive thresholding * Adaptive thresholding
*/ */
static uint8_t otsu(const struct quirc *q) #define THRESHOLD_S_DEN 8
#define THRESHOLD_T 5
static void threshold(struct quirc *q)
{ {
int numPixels = q->w * q->h; int x, y;
int avg_w = 0;
int avg_u = 0;
int threshold_s = q->w / THRESHOLD_S_DEN;
quirc_pixel_t *row = q->pixels;
// Calculate histogram for (y = 0; y < q->h; y++) {
const int HISTOGRAM_SIZE = 256; int row_average[q->w];
unsigned int histogram[HISTOGRAM_SIZE];
memset(histogram, 0, (HISTOGRAM_SIZE) * sizeof(unsigned int));
uint8_t* ptr = q->image;
int length = numPixels;
while (length--) {
uint8_t value = *ptr++;
histogram[value]++;
}
// Calculate weighted sum of histogram values memset(row_average, 0, sizeof(row_average));
int sum = 0;
for (int i = 0; i < HISTOGRAM_SIZE; ++i) {
sum += i * histogram[i];
}
// Compute threshold for (x = 0; x < q->w; x++) {
int sumB = 0; int w, u;
int q1 = 0;
double max = 0;
uint8_t threshold = 0;
for (int i = 0; i < HISTOGRAM_SIZE; ++i) {
// Weighted background
q1 += histogram[i];
if (q1 == 0)
continue;
// Weighted foreground if (y & 1) {
const int q2 = numPixels - q1; w = x;
if (q2 == 0) u = q->w - 1 - x;
break; } else {
w = q->w - 1 - x;
u = x;
}
sumB += i * histogram[i]; avg_w = (avg_w * (threshold_s - 1)) /
const double m1 = (double)sumB / q1; threshold_s + row[w];
const double m2 = ((double)sum - sumB) / q2; avg_u = (avg_u * (threshold_s - 1)) /
const double m1m2 = m1 - m2; threshold_s + row[u];
const double variance = m1m2 * m1m2 * q1 * q2;
if (variance >= max) { row_average[w] += avg_w;
threshold = i; row_average[u] += avg_u;
max = variance;
} }
}
return threshold; for (x = 0; x < q->w; x++) {
if (row[x] < row_average[x] *
(100 - THRESHOLD_T) / (200 * threshold_s))
row[x] = QUIRC_PIXEL_BLACK;
else
row[x] = QUIRC_PIXEL_WHITE;
}
row += q->w;
}
} }
static void area_count(void *user_data, int y, int left, int right) static void area_count(void *user_data, int y, int left, int right)
{ {
(void)y; (void)y;
((struct quirc_region *)user_data)->count += right - left + 1; ((struct quirc_region *)user_data)->count += right - left + 1;
} }
@@ -261,7 +381,7 @@ static int region_code(struct quirc *q, int x, int y)
box->seed.y = y; box->seed.y = y;
box->capstone = -1; box->capstone = -1;
flood_fill_seed(q, x, y, pixel, region, area_count, box, 0); flood_fill_seed(q, x, y, pixel, region, area_count, box);
return region; return region;
} }
@@ -331,7 +451,7 @@ static void find_region_corners(struct quirc *q,
psd.scores[0] = -1; psd.scores[0] = -1;
flood_fill_seed(q, region->seed.x, region->seed.y, flood_fill_seed(q, region->seed.x, region->seed.y,
rcode, QUIRC_PIXEL_BLACK, rcode, QUIRC_PIXEL_BLACK,
find_one_corner, &psd, 0); find_one_corner, &psd);
psd.ref.x = psd.corners[0].x - psd.ref.x; psd.ref.x = psd.corners[0].x - psd.ref.x;
psd.ref.y = psd.corners[0].y - psd.ref.y; psd.ref.y = psd.corners[0].y - psd.ref.y;
@@ -349,7 +469,7 @@ static void find_region_corners(struct quirc *q,
flood_fill_seed(q, region->seed.x, region->seed.y, flood_fill_seed(q, region->seed.x, region->seed.y,
QUIRC_PIXEL_BLACK, rcode, QUIRC_PIXEL_BLACK, rcode,
find_other_corners, &psd, 0); find_other_corners, &psd);
} }
static void record_capstone(struct quirc *q, int ring, int stone) static void record_capstone(struct quirc *q, int ring, int stone)
@@ -422,7 +542,7 @@ static void finder_scan(struct quirc *q, int y)
{ {
quirc_pixel_t *row = q->pixels + y * q->w; quirc_pixel_t *row = q->pixels + y * q->w;
int x; int x;
int last_color = 0; int last_color;
int run_length = 0; int run_length = 0;
int run_count = 0; int run_count = 0;
int pb[5]; int pb[5];
@@ -768,7 +888,7 @@ static int fitness_all(const struct quirc *q, int index)
/* Check alignment patterns */ /* Check alignment patterns */
ap_count = 0; ap_count = 0;
while ((ap_count < QUIRC_MAX_ALIGNMENT) && info->apat[ap_count]) while (info->apat[ap_count])
ap_count++; ap_count++;
for (i = 1; i + 1 < ap_count; i++) { for (i = 1; i + 1 < ap_count; i++) {
@@ -854,7 +974,7 @@ static void rotate_capstone(struct quirc_capstone *cap,
struct quirc_point copy[4]; struct quirc_point copy[4];
int j; int j;
int best = 0; int best = 0;
int best_score = INT_MAX; int best_score = 0;
for (j = 0; j < 4; j++) { for (j = 0; j < 4; j++) {
struct quirc_point *p = &cap->corners[j]; struct quirc_point *p = &cap->corners[j];
@@ -962,10 +1082,10 @@ static void record_qr_grid(struct quirc *q, int a, int b, int c)
flood_fill_seed(q, reg->seed.x, reg->seed.y, flood_fill_seed(q, reg->seed.x, reg->seed.y,
qr->align_region, QUIRC_PIXEL_BLACK, qr->align_region, QUIRC_PIXEL_BLACK,
NULL, NULL, 0); NULL, NULL);
flood_fill_seed(q, reg->seed.x, reg->seed.y, flood_fill_seed(q, reg->seed.x, reg->seed.y,
QUIRC_PIXEL_BLACK, qr->align_region, QUIRC_PIXEL_BLACK, qr->align_region,
find_leftmost_to_line, &psd, 0); find_leftmost_to_line, &psd);
} }
} }
@@ -1071,18 +1191,17 @@ static void test_grouping(struct quirc *q, int i)
test_neighbours(q, i, &hlist, &vlist); test_neighbours(q, i, &hlist, &vlist);
} }
static void pixels_setup(struct quirc *q, uint8_t threshold) static void pixels_setup(struct quirc *q)
{ {
if (QUIRC_PIXEL_ALIAS_IMAGE) { if (sizeof(*q->image) == sizeof(*q->pixels)) {
q->pixels = (quirc_pixel_t *)q->image; q->pixels = (quirc_pixel_t *)q->image;
} } else {
int x, y;
uint8_t* source = q->image; for (y = 0; y < q->h; y++) {
quirc_pixel_t* dest = q->pixels; for (x = 0; x < q->w; x++) {
int length = q->w * q->h; q->pixels[y * q->w + x] = q->image[y * q->w + x];
while (length--) { }
uint8_t value = *source++; }
*dest++ = (value < threshold) ? QUIRC_PIXEL_BLACK : QUIRC_PIXEL_WHITE;
} }
} }
@@ -1104,8 +1223,8 @@ void quirc_end(struct quirc *q)
{ {
int i; int i;
uint8_t threshold = otsu(q); pixels_setup(q);
pixels_setup(q, threshold); threshold(q);
for (i = 0; i < q->h; i++) for (i = 0; i < q->h; i++)
finder_scan(q, i); finder_scan(q, i);

View File

@@ -36,73 +36,34 @@ struct quirc *quirc_new(void)
void quirc_destroy(struct quirc *q) void quirc_destroy(struct quirc *q)
{ {
free(q->image); if (q->image)
/* q->pixels may alias q->image when their type representation is of the free(q->image);
same size, so we need to be careful here to avoid a double free */ if (sizeof(*q->image) != sizeof(*q->pixels))
if (!QUIRC_PIXEL_ALIAS_IMAGE)
free(q->pixels); free(q->pixels);
free(q); free(q);
} }
int quirc_resize(struct quirc *q, int w, int h) int quirc_resize(struct quirc *q, int w, int h)
{ {
uint8_t *image = NULL; uint8_t *new_image = realloc(q->image, w * h);
quirc_pixel_t *pixels = NULL;
/* if (!new_image)
* XXX: w and h should be size_t (or at least unsigned) as negatives return -1;
* values would not make much sense. The downside is that it would break
* both the API and ABI. Thus, at the moment, let's just do a sanity
* check.
*/
if (w < 0 || h < 0)
goto fail;
/* if (sizeof(*q->image) != sizeof(*q->pixels)) {
* alloc a new buffer for q->image. We avoid realloc(3) because we want size_t new_size = w * h * sizeof(quirc_pixel_t);
* on failure to be leave `q` in a consistant, unmodified state. quirc_pixel_t *new_pixels = realloc(q->pixels, new_size);
*/ if (!new_pixels)
image = calloc(w, h); return -1;
if (!image) q->pixels = new_pixels;
goto fail;
/* compute the "old" (i.e. currently allocated) and the "new"
(i.e. requested) image dimensions */
size_t olddim = q->w * q->h;
size_t newdim = w * h;
size_t min = (olddim < newdim ? olddim : newdim);
/*
* copy the data into the new buffer, avoiding (a) to read beyond the
* old buffer when the new size is greater and (b) to write beyond the
* new buffer when the new size is smaller, hence the min computation.
*/
(void)memcpy(image, q->image, min);
/* alloc a new buffer for q->pixels if needed */
if (!QUIRC_PIXEL_ALIAS_IMAGE) {
pixels = calloc(newdim, sizeof(quirc_pixel_t));
if (!pixels)
goto fail;
} }
/* alloc succeeded, update `q` with the new size and buffers */ q->image = new_image;
q->w = w; q->w = w;
q->h = h; q->h = h;
free(q->image);
q->image = image;
if (!QUIRC_PIXEL_ALIAS_IMAGE) {
free(q->pixels);
q->pixels = pixels;
}
return 0; return 0;
/* NOTREACHED */
fail:
free(image);
free(pixels);
return -1;
} }
int quirc_count(const struct quirc *q) int quirc_count(const struct quirc *q)
@@ -123,6 +84,7 @@ static const char *const error_table[] = {
const char *quirc_strerror(quirc_decode_error_t err) const char *quirc_strerror(quirc_decode_error_t err)
{ {
// note from Anemone3DS dev - L88 used to compare err >= 0, but err is always positive
if (err < sizeof(error_table) / sizeof(error_table[0])) if (err < sizeof(error_table) / sizeof(error_table[0]))
return error_table[err]; return error_table[err];

View File

@@ -19,10 +19,6 @@
#include <stdint.h> #include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
struct quirc; struct quirc;
/* Obtain the library version string. */ /* Obtain the library version string. */
@@ -121,7 +117,7 @@ struct quirc_code {
* is a bitmask giving the actual values of cells. If the cell * is a bitmask giving the actual values of cells. If the cell
* at (x, y) is black, then the following bit is set: * at (x, y) is black, then the following bit is set:
* *
* cell_bitmap[i >> 3] & (1 << (i & 7)) * cell_bitmap[i << 3] & (1 << (i & 7))
* *
* where i = (y * size) + x. * where i = (y * size) + x.
*/ */
@@ -166,8 +162,4 @@ void quirc_extract(const struct quirc *q, int index,
quirc_decode_error_t quirc_decode(const struct quirc_code *code, quirc_decode_error_t quirc_decode(const struct quirc_code *code,
struct quirc_data *data); struct quirc_data *data);
#ifdef __cplusplus
}
#endif
#endif #endif

View File

@@ -32,10 +32,8 @@
#define QUIRC_PERSPECTIVE_PARAMS 8 #define QUIRC_PERSPECTIVE_PARAMS 8
#if QUIRC_MAX_REGIONS < UINT8_MAX #if QUIRC_MAX_REGIONS < UINT8_MAX
#define QUIRC_PIXEL_ALIAS_IMAGE 1
typedef uint8_t quirc_pixel_t; typedef uint8_t quirc_pixel_t;
#elif QUIRC_MAX_REGIONS < UINT16_MAX #elif QUIRC_MAX_REGIONS < UINT16_MAX
#define QUIRC_PIXEL_ALIAS_IMAGE 0
typedef uint16_t quirc_pixel_t; typedef uint16_t quirc_pixel_t;
#else #else
#error "QUIRC_MAX_REGIONS > 65534 is not supported" #error "QUIRC_MAX_REGIONS > 65534 is not supported"
@@ -100,9 +98,9 @@ struct quirc {
#define QUIRC_MAX_ALIGNMENT 7 #define QUIRC_MAX_ALIGNMENT 7
struct quirc_rs_params { struct quirc_rs_params {
int bs; /* Small block size */ int bs; /* Block size */
int dw; /* Small data words */ int dw; /* Data words */
int ns; /* Number of small blocks */ int ce; /* Correctable errors */
}; };
struct quirc_version_info { struct quirc_version_info {

View File

@@ -22,400 +22,400 @@ const struct quirc_version_info quirc_version_db[QUIRC_MAX_VERSION + 1] = {
.data_bytes = 26, .data_bytes = 26,
.apat = {0}, .apat = {0},
.ecc = { .ecc = {
{.bs = 26, .dw = 16, .ns = 1}, {.bs = 26, .dw = 16, .ce = 4},
{.bs = 26, .dw = 19, .ns = 1}, {.bs = 26, .dw = 19, .ce = 2},
{.bs = 26, .dw = 9, .ns = 1}, {.bs = 26, .dw = 9, .ce = 8},
{.bs = 26, .dw = 13, .ns = 1} {.bs = 26, .dw = 13, .ce = 6}
} }
}, },
{ /* Version 2 */ { /* Version 2 */
.data_bytes = 44, .data_bytes = 44,
.apat = {6, 18, 0}, .apat = {6, 18, 0},
.ecc = { .ecc = {
{.bs = 44, .dw = 28, .ns = 1}, {.bs = 44, .dw = 28, .ce = 8},
{.bs = 44, .dw = 34, .ns = 1}, {.bs = 44, .dw = 34, .ce = 4},
{.bs = 44, .dw = 16, .ns = 1}, {.bs = 44, .dw = 16, .ce = 14},
{.bs = 44, .dw = 22, .ns = 1} {.bs = 44, .dw = 22, .ce = 11}
} }
}, },
{ /* Version 3 */ { /* Version 3 */
.data_bytes = 70, .data_bytes = 70,
.apat = {6, 22, 0}, .apat = {6, 22, 0},
.ecc = { .ecc = {
{.bs = 70, .dw = 44, .ns = 1}, {.bs = 70, .dw = 44, .ce = 13},
{.bs = 70, .dw = 55, .ns = 1}, {.bs = 70, .dw = 55, .ce = 7},
{.bs = 35, .dw = 13, .ns = 2}, {.bs = 35, .dw = 13, .ce = 11},
{.bs = 35, .dw = 17, .ns = 2} {.bs = 35, .dw = 17, .ce = 9}
} }
}, },
{ /* Version 4 */ { /* Version 4 */
.data_bytes = 100, .data_bytes = 100,
.apat = {6, 26, 0}, .apat = {6, 26, 0},
.ecc = { .ecc = {
{.bs = 50, .dw = 32, .ns = 2}, {.bs = 50, .dw = 32, .ce = 9},
{.bs = 100, .dw = 80, .ns = 1}, {.bs = 100, .dw = 80, .ce = 10},
{.bs = 25, .dw = 9, .ns = 4}, {.bs = 25, .dw = 9, .ce = 8},
{.bs = 50, .dw = 24, .ns = 2} {.bs = 50, .dw = 24, .ce = 13}
} }
}, },
{ /* Version 5 */ { /* Version 5 */
.data_bytes = 134, .data_bytes = 134,
.apat = {6, 30, 0}, .apat = {6, 30, 0},
.ecc = { .ecc = {
{.bs = 67, .dw = 43, .ns = 2}, {.bs = 67, .dw = 43, .ce = 12},
{.bs = 134, .dw = 108, .ns = 1}, {.bs = 134, .dw = 108, .ce = 13},
{.bs = 33, .dw = 11, .ns = 2}, {.bs = 33, .dw = 11, .ce = 11},
{.bs = 33, .dw = 15, .ns = 2} {.bs = 33, .dw = 15, .ce = 9}
} }
}, },
{ /* Version 6 */ { /* Version 6 */
.data_bytes = 172, .data_bytes = 172,
.apat = {6, 34, 0}, .apat = {6, 34, 0},
.ecc = { .ecc = {
{.bs = 43, .dw = 27, .ns = 4}, {.bs = 43, .dw = 27, .ce = 8},
{.bs = 86, .dw = 68, .ns = 2}, {.bs = 86, .dw = 68, .ce = 9},
{.bs = 43, .dw = 15, .ns = 4}, {.bs = 43, .dw = 15, .ce = 14},
{.bs = 43, .dw = 19, .ns = 4} {.bs = 43, .dw = 19, .ce = 12}
} }
}, },
{ /* Version 7 */ { /* Version 7 */
.data_bytes = 196, .data_bytes = 196,
.apat = {6, 22, 38, 0}, .apat = {6, 22, 38, 0},
.ecc = { .ecc = {
{.bs = 49, .dw = 31, .ns = 4}, {.bs = 49, .dw = 31, .ce = 9},
{.bs = 98, .dw = 78, .ns = 2}, {.bs = 98, .dw = 78, .ce = 10},
{.bs = 39, .dw = 13, .ns = 4}, {.bs = 39, .dw = 13, .ce = 13},
{.bs = 32, .dw = 14, .ns = 2} {.bs = 32, .dw = 14, .ce = 9}
} }
}, },
{ /* Version 8 */ { /* Version 8 */
.data_bytes = 242, .data_bytes = 242,
.apat = {6, 24, 42, 0}, .apat = {6, 24, 42, 0},
.ecc = { .ecc = {
{.bs = 60, .dw = 38, .ns = 2}, {.bs = 60, .dw = 38, .ce = 11},
{.bs = 121, .dw = 97, .ns = 2}, {.bs = 121, .dw = 97, .ce = 12},
{.bs = 40, .dw = 14, .ns = 4}, {.bs = 40, .dw = 14, .ce = 13},
{.bs = 40, .dw = 18, .ns = 4} {.bs = 40, .dw = 18, .ce = 11}
} }
}, },
{ /* Version 9 */ { /* Version 9 */
.data_bytes = 292, .data_bytes = 292,
.apat = {6, 26, 46, 0}, .apat = {6, 26, 46, 0},
.ecc = { .ecc = {
{.bs = 58, .dw = 36, .ns = 3}, {.bs = 58, .dw = 36, .ce = 11},
{.bs = 146, .dw = 116, .ns = 2}, {.bs = 146, .dw = 116, .ce = 15},
{.bs = 36, .dw = 12, .ns = 4}, {.bs = 36, .dw = 12, .ce = 12},
{.bs = 36, .dw = 16, .ns = 4} {.bs = 36, .dw = 16, .ce = 10}
} }
}, },
{ /* Version 10 */ { /* Version 10 */
.data_bytes = 346, .data_bytes = 346,
.apat = {6, 28, 50, 0}, .apat = {6, 28, 50, 0},
.ecc = { .ecc = {
{.bs = 69, .dw = 43, .ns = 4}, {.bs = 69, .dw = 43, .ce = 13},
{.bs = 86, .dw = 68, .ns = 2}, {.bs = 86, .dw = 68, .ce = 9},
{.bs = 43, .dw = 15, .ns = 6}, {.bs = 43, .dw = 15, .ce = 14},
{.bs = 43, .dw = 19, .ns = 6} {.bs = 43, .dw = 19, .ce = 12}
} }
}, },
{ /* Version 11 */ { /* Version 11 */
.data_bytes = 404, .data_bytes = 404,
.apat = {6, 30, 54, 0}, .apat = {6, 30, 54, 0},
.ecc = { .ecc = {
{.bs = 80, .dw = 50, .ns = 1}, {.bs = 80, .dw = 50, .ce = 15},
{.bs = 101, .dw = 81, .ns = 4}, {.bs = 101, .dw = 81, .ce = 10},
{.bs = 36, .dw = 12, .ns = 3}, {.bs = 36, .dw = 12, .ce = 12},
{.bs = 50, .dw = 22, .ns = 4} {.bs = 50, .dw = 22, .ce = 14}
} }
}, },
{ /* Version 12 */ { /* Version 12 */
.data_bytes = 466, .data_bytes = 466,
.apat = {6, 32, 58, 0}, .apat = {6, 32, 58, 0},
.ecc = { .ecc = {
{.bs = 58, .dw = 36, .ns = 6}, {.bs = 58, .dw = 36, .ce = 11},
{.bs = 116, .dw = 92, .ns = 2}, {.bs = 116, .dw = 92, .ce = 12},
{.bs = 42, .dw = 14, .ns = 7}, {.bs = 42, .dw = 14, .ce = 14},
{.bs = 46, .dw = 20, .ns = 4} {.bs = 46, .dw = 20, .ce = 14}
} }
}, },
{ /* Version 13 */ { /* Version 13 */
.data_bytes = 532, .data_bytes = 532,
.apat = {6, 34, 62, 0}, .apat = {6, 34, 62, 0},
.ecc = { .ecc = {
{.bs = 59, .dw = 37, .ns = 8}, {.bs = 59, .dw = 37, .ce = 11},
{.bs = 133, .dw = 107, .ns = 4}, {.bs = 133, .dw = 107, .ce = 13},
{.bs = 33, .dw = 11, .ns = 12}, {.bs = 33, .dw = 11, .ce = 11},
{.bs = 44, .dw = 20, .ns = 8} {.bs = 44, .dw = 20, .ce = 12}
} }
}, },
{ /* Version 14 */ { /* Version 14 */
.data_bytes = 581, .data_bytes = 581,
.apat = {6, 26, 46, 66, 0}, .apat = {6, 26, 46, 66, 0},
.ecc = { .ecc = {
{.bs = 64, .dw = 40, .ns = 4}, {.bs = 65, .dw = 41, .ce = 12},
{.bs = 145, .dw = 115, .ns = 3}, {.bs = 109, .dw = 87, .ce = 11},
{.bs = 36, .dw = 12, .ns = 11}, {.bs = 36, .dw = 12, .ce = 12},
{.bs = 36, .dw = 16, .ns = 11} {.bs = 54, .dw = 24, .ce = 15}
} }
}, },
{ /* Version 15 */ { /* Version 15 */
.data_bytes = 655, .data_bytes = 655,
.apat = {6, 26, 48, 70, 0}, .apat = {6, 26, 48, 70, 0},
.ecc = { .ecc = {
{.bs = 65, .dw = 41, .ns = 5}, {.bs = 65, .dw = 41, .ce = 12},
{.bs = 109, .dw = 87, .ns = 5}, {.bs = 109, .dw = 87, .ce = 11},
{.bs = 36, .dw = 12, .ns = 11}, {.bs = 36, .dw = 12, .ce = 12},
{.bs = 54, .dw = 24, .ns = 5} {.bs = 54, .dw = 24, .ce = 15}
} }
}, },
{ /* Version 16 */ { /* Version 16 */
.data_bytes = 733, .data_bytes = 733,
.apat = {6, 26, 50, 74, 0}, .apat = {6, 26, 50, 74, 0},
.ecc = { .ecc = {
{.bs = 73, .dw = 45, .ns = 7}, {.bs = 73, .dw = 45, .ce = 14},
{.bs = 122, .dw = 98, .ns = 5}, {.bs = 122, .dw = 98, .ce = 12},
{.bs = 45, .dw = 15, .ns = 3}, {.bs = 45, .dw = 15, .ce = 15},
{.bs = 43, .dw = 19, .ns = 15} {.bs = 43, .dw = 19, .ce = 12}
} }
}, },
{ /* Version 17 */ { /* Version 17 */
.data_bytes = 815, .data_bytes = 815,
.apat = {6, 30, 54, 78, 0}, .apat = {6, 30, 54, 78, 0},
.ecc = { .ecc = {
{.bs = 74, .dw = 46, .ns = 10}, {.bs = 74, .dw = 46, .ce = 14},
{.bs = 135, .dw = 107, .ns = 1}, {.bs = 135, .dw = 107, .ce = 14},
{.bs = 42, .dw = 14, .ns = 2}, {.bs = 42, .dw = 14, .ce = 14},
{.bs = 50, .dw = 22, .ns = 1} {.bs = 50, .dw = 22, .ce = 14}
} }
}, },
{ /* Version 18 */ { /* Version 18 */
.data_bytes = 901, .data_bytes = 901,
.apat = {6, 30, 56, 82, 0}, .apat = {6, 30, 56, 82, 0},
.ecc = { .ecc = {
{.bs = 69, .dw = 43, .ns = 9}, {.bs = 69, .dw = 43, .ce = 13},
{.bs = 150, .dw = 120, .ns = 5}, {.bs = 150, .dw = 120, .ce = 15},
{.bs = 42, .dw = 14, .ns = 2}, {.bs = 42, .dw = 14, .ce = 14},
{.bs = 50, .dw = 22, .ns = 17} {.bs = 50, .dw = 22, .ce = 14}
} }
}, },
{ /* Version 19 */ { /* Version 19 */
.data_bytes = 991, .data_bytes = 991,
.apat = {6, 30, 58, 86, 0}, .apat = {6, 30, 58, 86, 0},
.ecc = { .ecc = {
{.bs = 70, .dw = 44, .ns = 3}, {.bs = 70, .dw = 44, .ce = 13},
{.bs = 141, .dw = 113, .ns = 3}, {.bs = 141, .dw = 113, .ce = 14},
{.bs = 39, .dw = 13, .ns = 9}, {.bs = 39, .dw = 13, .ce = 13},
{.bs = 47, .dw = 21, .ns = 17} {.bs = 47, .dw = 21, .ce = 13}
} }
}, },
{ /* Version 20 */ { /* Version 20 */
.data_bytes = 1085, .data_bytes = 1085,
.apat = {6, 34, 62, 90, 0}, .apat = {6, 34, 62, 90, 0},
.ecc = { .ecc = {
{.bs = 67, .dw = 41, .ns = 3}, {.bs = 67, .dw = 41, .ce = 13},
{.bs = 135, .dw = 107, .ns = 3}, {.bs = 135, .dw = 107, .ce = 14},
{.bs = 43, .dw = 15, .ns = 15}, {.bs = 43, .dw = 15, .ce = 14},
{.bs = 54, .dw = 24, .ns = 15} {.bs = 54, .dw = 24, .ce = 15}
} }
}, },
{ /* Version 21 */ { /* Version 21 */
.data_bytes = 1156, .data_bytes = 1156,
.apat = {6, 28, 50, 72, 92, 0}, .apat = {6, 28, 50, 72, 92, 0},
.ecc = { .ecc = {
{.bs = 68, .dw = 42, .ns = 17}, {.bs = 68, .dw = 42, .ce = 13},
{.bs = 144, .dw = 116, .ns = 4}, {.bs = 144, .dw = 116, .ce = 14},
{.bs = 46, .dw = 16, .ns = 19}, {.bs = 46, .dw = 16, .ce = 15},
{.bs = 50, .dw = 22, .ns = 17} {.bs = 50, .dw = 22, .ce = 14}
} }
}, },
{ /* Version 22 */ { /* Version 22 */
.data_bytes = 1258, .data_bytes = 1258,
.apat = {6, 26, 50, 74, 98, 0}, .apat = {6, 26, 50, 74, 98, 0},
.ecc = { .ecc = {
{.bs = 74, .dw = 46, .ns = 17}, {.bs = 74, .dw = 46, .ce = 14},
{.bs = 139, .dw = 111, .ns = 2}, {.bs = 139, .dw = 111, .ce = 14},
{.bs = 37, .dw = 13, .ns = 34}, {.bs = 37, .dw = 13, .ce = 12},
{.bs = 54, .dw = 24, .ns = 7} {.bs = 54, .dw = 24, .ce = 15}
} }
}, },
{ /* Version 23 */ { /* Version 23 */
.data_bytes = 1364, .data_bytes = 1364,
.apat = {6, 30, 54, 78, 102, 0}, .apat = {6, 30, 54, 78, 102, 0},
.ecc = { .ecc = {
{.bs = 75, .dw = 47, .ns = 4}, {.bs = 75, .dw = 47, .ce = 14},
{.bs = 151, .dw = 121, .ns = 4}, {.bs = 151, .dw = 121, .ce = 15},
{.bs = 45, .dw = 15, .ns = 16}, {.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ns = 11} {.bs = 54, .dw = 24, .ce = 15}
} }
}, },
{ /* Version 24 */ { /* Version 24 */
.data_bytes = 1474, .data_bytes = 1474,
.apat = {6, 28, 54, 80, 106, 0}, .apat = {6, 28, 54, 80, 106, 0},
.ecc = { .ecc = {
{.bs = 73, .dw = 45, .ns = 6}, {.bs = 73, .dw = 45, .ce = 14},
{.bs = 147, .dw = 117, .ns = 6}, {.bs = 147, .dw = 117, .ce = 15},
{.bs = 46, .dw = 16, .ns = 30}, {.bs = 46, .dw = 16, .ce = 15},
{.bs = 54, .dw = 24, .ns = 11} {.bs = 54, .dw = 24, .ce = 15}
} }
}, },
{ /* Version 25 */ { /* Version 25 */
.data_bytes = 1588, .data_bytes = 1588,
.apat = {6, 32, 58, 84, 110, 0}, .apat = {6, 32, 58, 84, 110, 0},
.ecc = { .ecc = {
{.bs = 75, .dw = 47, .ns = 8}, {.bs = 75, .dw = 47, .ce = 14},
{.bs = 132, .dw = 106, .ns = 8}, {.bs = 132, .dw = 106, .ce = 13},
{.bs = 45, .dw = 15, .ns = 22}, {.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ns = 7} {.bs = 54, .dw = 24, .ce = 15}
} }
}, },
{ /* Version 26 */ { /* Version 26 */
.data_bytes = 1706, .data_bytes = 1706,
.apat = {6, 30, 58, 86, 114, 0}, .apat = {6, 30, 58, 86, 114, 0},
.ecc = { .ecc = {
{.bs = 74, .dw = 46, .ns = 19}, {.bs = 74, .dw = 46, .ce = 14},
{.bs = 142, .dw = 114, .ns = 10}, {.bs = 142, .dw = 114, .ce = 14},
{.bs = 46, .dw = 16, .ns = 33}, {.bs = 46, .dw = 16, .ce = 15},
{.bs = 50, .dw = 22, .ns = 28} {.bs = 50, .dw = 22, .ce = 14}
} }
}, },
{ /* Version 27 */ { /* Version 27 */
.data_bytes = 1828, .data_bytes = 1828,
.apat = {6, 34, 62, 90, 118, 0}, .apat = {6, 34, 62, 90, 118, 0},
.ecc = { .ecc = {
{.bs = 73, .dw = 45, .ns = 22}, {.bs = 73, .dw = 45, .ce = 14},
{.bs = 152, .dw = 122, .ns = 8}, {.bs = 152, .dw = 122, .ce = 15},
{.bs = 45, .dw = 15, .ns = 12}, {.bs = 45, .dw = 15, .ce = 15},
{.bs = 53, .dw = 23, .ns = 8} {.bs = 53, .dw = 23, .ce = 15}
} }
}, },
{ /* Version 28 */ { /* Version 28 */
.data_bytes = 1921, .data_bytes = 1921,
.apat = {6, 26, 50, 74, 98, 122, 0}, .apat = {6, 26, 50, 74, 98, 122, 0},
.ecc = { .ecc = {
{.bs = 73, .dw = 45, .ns = 3}, {.bs = 73, .dw = 45, .ce = 14},
{.bs = 147, .dw = 117, .ns = 3}, {.bs = 147, .dw = 117, .ce = 15},
{.bs = 45, .dw = 15, .ns = 11}, {.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ns = 4} {.bs = 54, .dw = 24, .ce = 15}
} }
}, },
{ /* Version 29 */ { /* Version 29 */
.data_bytes = 2051, .data_bytes = 2051,
.apat = {6, 30, 54, 78, 102, 126, 0}, .apat = {6, 30, 54, 78, 102, 126, 0},
.ecc = { .ecc = {
{.bs = 73, .dw = 45, .ns = 21}, {.bs = 73, .dw = 45, .ce = 14},
{.bs = 146, .dw = 116, .ns = 7}, {.bs = 146, .dw = 116, .ce = 15},
{.bs = 45, .dw = 15, .ns = 19}, {.bs = 45, .dw = 15, .ce = 15},
{.bs = 53, .dw = 23, .ns = 1} {.bs = 73, .dw = 45, .ce = 14}
} }
}, },
{ /* Version 30 */ { /* Version 30 */
.data_bytes = 2185, .data_bytes = 2185,
.apat = {6, 26, 52, 78, 104, 130, 0}, .apat = {6, 26, 52, 78, 104, 130, 0},
.ecc = { .ecc = {
{.bs = 75, .dw = 47, .ns = 19}, {.bs = 75, .dw = 47, .ce = 14},
{.bs = 145, .dw = 115, .ns = 5}, {.bs = 145, .dw = 115, .ce = 15},
{.bs = 45, .dw = 15, .ns = 23}, {.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ns = 15} {.bs = 54, .dw = 24, .ce = 15}
} }
}, },
{ /* Version 31 */ { /* Version 31 */
.data_bytes = 2323, .data_bytes = 2323,
.apat = {6, 30, 56, 82, 108, 134, 0}, .apat = {6, 30, 56, 82, 108, 134, 0},
.ecc = { .ecc = {
{.bs = 74, .dw = 46, .ns = 2}, {.bs = 74, .dw = 46, .ce = 14},
{.bs = 145, .dw = 115, .ns = 13}, {.bs = 145, .dw = 115, .ce = 15},
{.bs = 45, .dw = 15, .ns = 23}, {.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ns = 42} {.bs = 54, .dw = 24, .ce = 15}
} }
}, },
{ /* Version 32 */ { /* Version 32 */
.data_bytes = 2465, .data_bytes = 2465,
.apat = {6, 34, 60, 86, 112, 138, 0}, .apat = {6, 34, 60, 86, 112, 138, 0},
.ecc = { .ecc = {
{.bs = 74, .dw = 46, .ns = 10}, {.bs = 74, .dw = 46, .ce = 14},
{.bs = 145, .dw = 115, .ns = 17}, {.bs = 145, .dw = 115, .ce = 15},
{.bs = 45, .dw = 15, .ns = 19}, {.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ns = 10} {.bs = 54, .dw = 24, .ce = 15}
} }
}, },
{ /* Version 33 */ { /* Version 33 */
.data_bytes = 2611, .data_bytes = 2611,
.apat = {6, 30, 58, 86, 114, 142, 0}, .apat = {6, 30, 58, 96, 114, 142, 0},
.ecc = { .ecc = {
{.bs = 74, .dw = 46, .ns = 14}, {.bs = 74, .dw = 46, .ce = 14},
{.bs = 145, .dw = 115, .ns = 17}, {.bs = 145, .dw = 115, .ce = 15},
{.bs = 45, .dw = 15, .ns = 11}, {.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ns = 29} {.bs = 54, .dw = 24, .ce = 15}
} }
}, },
{ /* Version 34 */ { /* Version 34 */
.data_bytes = 2761, .data_bytes = 2761,
.apat = {6, 34, 62, 90, 118, 146, 0}, .apat = {6, 34, 62, 90, 118, 146, 0},
.ecc = { .ecc = {
{.bs = 74, .dw = 46, .ns = 14}, {.bs = 74, .dw = 46, .ce = 14},
{.bs = 145, .dw = 115, .ns = 13}, {.bs = 145, .dw = 115, .ce = 15},
{.bs = 46, .dw = 16, .ns = 59}, {.bs = 46, .dw = 16, .ce = 15},
{.bs = 54, .dw = 24, .ns = 44} {.bs = 54, .dw = 24, .ce = 15}
} }
}, },
{ /* Version 35 */ { /* Version 35 */
.data_bytes = 2876, .data_bytes = 2876,
.apat = {6, 30, 54, 78, 102, 126, 150}, .apat = {6, 30, 54, 78, 102, 126, 150},
.ecc = { .ecc = {
{.bs = 75, .dw = 47, .ns = 12}, {.bs = 75, .dw = 47, .ce = 14},
{.bs = 151, .dw = 121, .ns = 12}, {.bs = 151, .dw = 121, .ce = 15},
{.bs = 45, .dw = 15, .ns = 22}, {.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ns = 39} {.bs = 54, .dw = 24, .ce = 15}
} }
}, },
{ /* Version 36 */ { /* Version 36 */
.data_bytes = 3034, .data_bytes = 3034,
.apat = {6, 24, 50, 76, 102, 128, 154}, .apat = {6, 24, 50, 76, 102, 128, 154},
.ecc = { .ecc = {
{.bs = 75, .dw = 47, .ns = 6}, {.bs = 75, .dw = 47, .ce = 14},
{.bs = 151, .dw = 121, .ns = 6}, {.bs = 151, .dw = 121, .ce = 15},
{.bs = 45, .dw = 15, .ns = 2}, {.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ns = 46} {.bs = 54, .dw = 24, .ce = 15}
} }
}, },
{ /* Version 37 */ { /* Version 37 */
.data_bytes = 3196, .data_bytes = 3196,
.apat = {6, 28, 54, 80, 106, 132, 158}, .apat = {6, 28, 54, 80, 106, 132, 158},
.ecc = { .ecc = {
{.bs = 74, .dw = 46, .ns = 29}, {.bs = 74, .dw = 46, .ce = 14},
{.bs = 152, .dw = 122, .ns = 17}, {.bs = 152, .dw = 122, .ce = 15},
{.bs = 45, .dw = 15, .ns = 24}, {.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ns = 49} {.bs = 54, .dw = 24, .ce = 15}
} }
}, },
{ /* Version 38 */ { /* Version 38 */
.data_bytes = 3362, .data_bytes = 3362,
.apat = {6, 32, 58, 84, 110, 136, 162}, .apat = {6, 32, 58, 84, 110, 136, 162},
.ecc = { .ecc = {
{.bs = 74, .dw = 46, .ns = 13}, {.bs = 74, .dw = 46, .ce = 14},
{.bs = 152, .dw = 122, .ns = 4}, {.bs = 152, .dw = 122, .ce = 15},
{.bs = 45, .dw = 15, .ns = 42}, {.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ns = 48} {.bs = 54, .dw = 24, .ce = 15}
} }
}, },
{ /* Version 39 */ { /* Version 39 */
.data_bytes = 3532, .data_bytes = 3532,
.apat = {6, 26, 54, 82, 110, 138, 166}, .apat = {6, 26, 54, 82, 110, 138, 166},
.ecc = { .ecc = {
{.bs = 75, .dw = 47, .ns = 40}, {.bs = 75, .dw = 47, .ce = 14},
{.bs = 147, .dw = 117, .ns = 20}, {.bs = 147, .dw = 117, .ce = 15},
{.bs = 45, .dw = 15, .ns = 10}, {.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ns = 43} {.bs = 54, .dw = 24, .ce = 15}
} }
}, },
{ /* Version 40 */ { /* Version 40 */
.data_bytes = 3706, .data_bytes = 3706,
.apat = {6, 30, 58, 86, 114, 142, 170}, .apat = {6, 30, 58, 86, 114, 142, 170},
.ecc = { .ecc = {
{.bs = 75, .dw = 47, .ns = 18}, {.bs = 75, .dw = 47, .ce = 14},
{.bs = 148, .dw = 118, .ns = 19}, {.bs = 148, .dw = 118, .ce = 15},
{.bs = 45, .dw = 15, .ns = 20}, {.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ns = 34} {.bs = 54, .dw = 24, .ce = 15}
} }
} }
}; };

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -184,30 +184,16 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
if(installmode & THEME_INSTALL_BGM) if(installmode & THEME_INSTALL_BGM)
{ {
music_size = load_data("/bgm.bcstm", current_theme, &music); music_size = load_data("/bgm.bcstm", current_theme, &music);
if (music_size > BGM_MAX_SIZE) if(music_size > BGM_MAX_SIZE)
{ {
free(music); free(music);
DEBUG("bgm too big\n"); DEBUG("bgm too big\n");
return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_TOO_LARGE); return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_TOO_LARGE);
} }
if (music_size != 0) remake_file(fsMakePath(PATH_ASCII, "/BgmCache.bin"), ArchiveThemeExt, BGM_MAX_SIZE);
{ res = buf_to_file(music_size, fsMakePath(PATH_ASCII, "/BgmCache.bin"), ArchiveThemeExt, music);
remake_file(fsMakePath(PATH_ASCII, "/BgmCache.bin"), ArchiveThemeExt, BGM_MAX_SIZE); free(music);
res = buf_to_file(music_size, fsMakePath(PATH_ASCII, "/BgmCache.bin"), ArchiveThemeExt, music);
free(music);
char *body_buf = NULL;
u32 uncompressed_size = decompress_lz_file(fsMakePath(PATH_ASCII, "/BodyCache.bin"), ArchiveThemeExt, &body_buf);
if (body_buf[5] != 1)
{
installmode |= THEME_INSTALL_BODY;
body_buf[5] = 1;
body_size = compress_lz_file_fast(fsMakePath(PATH_ASCII, "/BodyCache.bin"), ArchiveThemeExt, body_buf, uncompressed_size);
}
free(body_buf);
}
if(R_FAILED(res)) return res; if(R_FAILED(res)) return res;
} else } else
@@ -261,24 +247,18 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
SaveData_dat_s* savedata = (SaveData_dat_s*)savedata_buf; SaveData_dat_s* savedata = (SaveData_dat_s*)savedata_buf;
memset(&savedata->theme_entry, 0, sizeof(ThemeEntry_s)); memset(&savedata->theme_entry, 0, sizeof(ThemeEntry_s));
savedata->theme_entry.type = 3;
savedata->theme_entry.index = 0xff;
savedata->shuffle = (installmode & THEME_INSTALL_SHUFFLE) ? 1 : 0; savedata->shuffle = (installmode & THEME_INSTALL_SHUFFLE);
memset(savedata->shuffle_themes, 0, sizeof(ThemeEntry_s)*MAX_SHUFFLE_THEMES);
if(installmode & THEME_INSTALL_SHUFFLE) if(installmode & THEME_INSTALL_SHUFFLE)
{ {
memset(savedata->shuffle_themes, 0, sizeof(ThemeEntry_s)*MAX_SHUFFLE_THEMES);
for(int i = 0; i < themes.shuffle_count; i++) for(int i = 0; i < themes.shuffle_count; i++)
{ {
savedata->shuffle_themes[i].type = 3; savedata->shuffle_themes[i].type = 3;
savedata->shuffle_themes[i].index = i; savedata->shuffle_themes[i].index = i;
} }
const u8 shuffle_seed[0xB] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
memcpy(savedata->shuffle_seedA, shuffle_seed, 0xB);
memcpy(savedata->shuffle_seedB, shuffle_seed, 0xA);
}
else
{
savedata->theme_entry.type = 3;
savedata->theme_entry.index = 0xff;
} }
res = buf_to_file(savedata_size, fsMakePath(PATH_ASCII, "/SaveData.dat"), ArchiveHomeExt, savedata_buf); res = buf_to_file(savedata_size, fsMakePath(PATH_ASCII, "/SaveData.dat"), ArchiveHomeExt, savedata_buf);
@@ -321,344 +301,6 @@ inline Result shuffle_install(Entry_List_s themes)
return install_theme_internal(themes, THEME_INSTALL_SHUFFLE | THEME_INSTALL_BODY | THEME_INSTALL_BGM); return install_theme_internal(themes, THEME_INSTALL_SHUFFLE | THEME_INSTALL_BODY | THEME_INSTALL_BGM);
} }
static SwkbdCallbackResult
dir_name_callback(void *data, const char ** ppMessage, const char * text, size_t textlen)
{
(void)textlen;
(void)data;
if(strpbrk(text, "><\"?;:/\\+,.|[=]"))
{
*ppMessage = "Illegal character used.";
return SWKBD_CALLBACK_CONTINUE;
}
return SWKBD_CALLBACK_OK;
}
Result dump_current_theme(void)
{
const int max_chars = 255;
char * output_dir = calloc(max_chars + 1, sizeof(char));
SwkbdState swkbd;
swkbdInit(&swkbd, SWKBD_TYPE_WESTERN, 2, max_chars);
swkbdSetHintText(&swkbd, "Name of output folder");
swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, "Cancel", false);
swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, "Done", true);
swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, 0, max_chars);
swkbdSetFilterCallback(&swkbd, dir_name_callback, NULL);
SwkbdButton button = swkbdInputText(&swkbd, output_dir, max_chars);
if (button != SWKBD_BUTTON_CONFIRM)
{
DEBUG("<dump_theme> Something went wrong with getting swkbd\n");
return MAKERESULT(RL_FATAL, RS_CANCELED, RM_UTIL, RD_CANCEL_REQUESTED);
}
u16 path[0x107] = { 0 };
struacat(path, "/themes/");
struacat(path, output_dir);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_UTF16, path), FS_ATTRIBUTE_DIRECTORY);
char *thememanage_buf = NULL;
file_to_buf(fsMakePath(PATH_ASCII, "/ThemeManage.bin"), ArchiveThemeExt, &thememanage_buf);
ThemeManage_bin_s * theme_manage = (ThemeManage_bin_s *)thememanage_buf;
u32 theme_size = theme_manage->body_size;
u32 bgm_size = theme_manage->music_size;
free(thememanage_buf);
char *temp_buf = NULL;
file_to_buf(fsMakePath(PATH_ASCII, "/BodyCache.bin"), ArchiveThemeExt, &temp_buf);
u16 path_output[0x107] = { 0 };
memcpy(path_output, path, 0x107);
struacat(path_output, "/body_LZ.bin");
remake_file(fsMakePath(PATH_UTF16, path_output), ArchiveSD, theme_size);
buf_to_file(theme_size, fsMakePath(PATH_UTF16, path_output), ArchiveSD, temp_buf);
free(temp_buf);
temp_buf = NULL;
file_to_buf(fsMakePath(PATH_ASCII, "/BgmCache.bin"), ArchiveThemeExt, &temp_buf);
memcpy(path_output, path, 0x107);
struacat(path_output, "/bgm.bcstm");
remake_file(fsMakePath(PATH_UTF16, path_output), ArchiveSD, bgm_size);
buf_to_file(bgm_size, fsMakePath(PATH_UTF16, path_output), ArchiveSD, temp_buf);
free(temp_buf);
temp_buf = NULL;
char *smdh_file = calloc(1, 0x36c0);
smdh_file[0] = 0x53; // SMDH magic
smdh_file[1] = 0x4d;
smdh_file[2] = 0x44;
smdh_file[3] = 0x48;
struacat((u16 *) (smdh_file + 0x8), output_dir);
struacat((u16 *) (smdh_file + 0x88), "No description");
struacat((u16 *) (smdh_file + 0x188), "Unknown Author");
free(output_dir);
u8 r = rand() % 255;
u8 g = rand() % 255;
u8 b = rand() % 255;
u16 color = ((r & 0b11111000) << 8) | ((g & 0b11111100) << 3) | (b >> 3);
for (int i = 0x2040; i < 0x36c0; i += 2)
{
*(smdh_file + i) = (color & 0xFF00) >> 8;
*(smdh_file + i + 1) = color & 0x00FF;
}
memcpy(path_output, path, 0x107);
struacat(path_output, "/info.smdh");
remake_file(fsMakePath(PATH_UTF16, path_output), ArchiveSD, 0x36c0);
buf_to_file(0x36c0, fsMakePath(PATH_UTF16, path_output), ArchiveSD, smdh_file);
free(smdh_file);
return 0;
}
Result dump_all_themes(void)
{
const u32 high_id = 0x0004008c;
u32 low_id = 0;
u8 regionCode, language;
Result res = CFGU_SecureInfoGetRegion(&regionCode);
if(R_FAILED(res))
return res;
res = CFGU_GetSystemLanguage(&language);
if(R_FAILED(res))
return res;
switch(regionCode)
{
case CFG_REGION_JPN:
low_id = 0x00008200;
break;
case CFG_REGION_USA:
low_id = 0x00008f00;
break;
case CFG_REGION_EUR:
low_id = 0x00009800;
break;
default:
return -1;
}
const char* region_arr[4] = {
"JPN",
"USA",
"EUR",
"AUS",
};
const char* language_arr[12] = {
"jp",
"en",
"fr",
"de",
"it",
"es",
"zh",
"ko",
"nl",
"pt",
"ru",
"tw",
};
res = amAppInit();
if(R_FAILED(res))
return res;
Icon_s* smdh_data = calloc(1, sizeof(Icon_s));
smdh_data->_padding1[0] = 0x53; // SMDH magic
smdh_data->_padding1[1] = 0x4d;
smdh_data->_padding1[2] = 0x44;
smdh_data->_padding1[3] = 0x48;
utf8_to_utf16(smdh_data->author, (u8*)"Nintendo", 0x40);
utf8_to_utf16(smdh_data->desc, (u8*)"Official theme. For personal use only. Do not redistribute.", 0x80);
for(u32 dlc_index = 0; dlc_index <= 0xFF; ++dlc_index)
{
const u64 titleId = ((u64)high_id << 32) | (low_id | dlc_index);
u32 count = 0;
res = AMAPP_GetDLCContentInfoCount(&count, MEDIATYPE_SD, titleId);
if(res == (Result)0xd8a083fa)
{
res = 0;
break;
}
else if(R_FAILED(res))
{
break;
}
AM_ContentInfo* contentInfos = calloc(count, sizeof(AM_ContentInfo));
u32 readcount = 0;
res = AMAPP_ListDLCContentInfos(&readcount, MEDIATYPE_SD, titleId, count, 0, contentInfos);
if(R_FAILED(res))
{
break;
}
u32 archivePath[4] = {low_id | dlc_index, high_id, MEDIATYPE_SD, 0};
FS_Path ncch_path;
ncch_path.type = PATH_BINARY;
ncch_path.size = 0x10;
ncch_path.data = archivePath;
FS_Archive ncch_archive;
res = FSUSER_OpenArchive(&ncch_archive, ARCHIVE_SAVEDATA_AND_CONTENT, ncch_path);
if(R_FAILED(res))
{
free(contentInfos);
break;
}
u32 metadataPath[5] = {0, 0, 0, 0, 0};
FS_Path metadata_path;
metadata_path.type = PATH_BINARY;
metadata_path.size = 0x14;
metadata_path.data = metadataPath;
Handle metadata_fh;
res = FSUSER_OpenFile(&metadata_fh, ncch_archive, metadata_path, FS_OPEN_READ, 0);
if(R_FAILED(res))
{
FSUSER_CloseArchive(ncch_archive);
free(contentInfos);
break;
}
res = romfsMountFromFile(metadata_fh, 0, "meta");
if(R_FAILED(res))
{
FSFILE_Close(metadata_fh);
FSUSER_CloseArchive(ncch_archive);
free(contentInfos);
break;
}
char contentinfoarchive_path[40] = {0};
sprintf(contentinfoarchive_path, "meta:/ContentInfoArchive_%s_%s.bin", region_arr[regionCode], language_arr[language]);
FILE* fh = fopen(contentinfoarchive_path, "rb");
for(u32 i = 0; i < readcount; ++i)
{
if(i == 0) continue;
AM_ContentInfo* content = &contentInfos[i];
if((content->flags & (AM_CONTENT_DOWNLOADED | AM_CONTENT_OWNED)) == (AM_CONTENT_DOWNLOADED | AM_CONTENT_OWNED))
{
long off = 0x8 + 0xC8 * i;
fseek(fh, off, SEEK_SET);
char content_data[0xc8] = {0};
fread(content_data, 1, 0xc8, fh);
u32 extra_index = 0;
memcpy(&extra_index, content_data + 0xC0, 4);
metadataPath[1] = content->index;
Handle theme_fh;
res = FSUSER_OpenFile(&theme_fh, ncch_archive, metadata_path, FS_OPEN_READ, 0);
if(R_FAILED(res))
{
DEBUG("theme open romfs error: %08lx\n", res);
fclose(fh);
free(contentInfos);
romfsUnmount("meta");
FSUSER_CloseArchive(ncch_archive);
free(contentInfos);
break;
}
romfsMountFromFile(theme_fh, 0, "theme");
char themename[0x41] = {0};
memcpy(themename, content_data, 0x40);
char * illegal_char = themename;
while ((illegal_char = strpbrk(illegal_char, ILLEGAL_CHARS)))
{
*illegal_char = '-';
}
char path[0x107] = { 0 };
sprintf(path, "/Themes/Dump-%02lx-%ld-%s", dlc_index, extra_index, themename);
DEBUG("theme folder to create: %s\n", path);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, path), FS_ATTRIBUTE_DIRECTORY);
memset(smdh_data->name, 0, sizeof(smdh_data->name));
utf8_to_utf16(smdh_data->name, (u8*)(content_data + 0), 0x40);
FILE* theme_file = fopen("theme:/body_LZ.bin", "rb");
if(theme_file)
{
fseek(theme_file, 0, SEEK_END);
long theme_size = ftell(theme_file);
fseek(theme_file, 0, SEEK_CUR);
char* theme_data = malloc(theme_size);
fread(theme_data, 1, theme_size, theme_file);
fclose(theme_file);
char themepath[0x107] = {0};
sprintf(themepath, "%s/body_LZ.bin", path);
remake_file(fsMakePath(PATH_ASCII, themepath), ArchiveSD, theme_size);
buf_to_file(theme_size, fsMakePath(PATH_ASCII, themepath), ArchiveSD, theme_data);
free(theme_data);
}
FILE* bgm_file = fopen("theme:/bgm.bcstm", "rb");
if(bgm_file)
{
fseek(bgm_file, 0, SEEK_END);
long bgm_size = ftell(bgm_file);
fseek(bgm_file, 0, SEEK_CUR);
char* bgm_data = malloc(bgm_size);
fread(bgm_data, 1, bgm_size, bgm_file);
fclose(bgm_file);
char bgmpath[0x107] = {0};
sprintf(bgmpath, "%s/bgm.bcstm", path);
remake_file(fsMakePath(PATH_ASCII, bgmpath), ArchiveSD, bgm_size);
buf_to_file(bgm_size, fsMakePath(PATH_ASCII, bgmpath), ArchiveSD, bgm_data);
free(bgm_data);
}
romfsUnmount("theme");
char icondatapath[0x107] = {0};
sprintf(icondatapath, "meta:/icons/%ld.icn", extra_index);
FILE* iconfile = fopen(icondatapath, "rb");
fread(smdh_data->big_icon, 1, sizeof(smdh_data->big_icon), iconfile);
fclose(iconfile);
strcat(path, "/info.smdh");
remake_file(fsMakePath(PATH_ASCII, path), ArchiveSD, 0x36c0);
buf_to_file(0x36c0, fsMakePath(PATH_ASCII, path), ArchiveSD, (char*)smdh_data);
}
}
fclose(fh);
fh = NULL;
free(contentInfos);
contentInfos = NULL;
romfsUnmount("meta");
// don't need to close the file opened for the metadata, romfsUnmount took ownership
FSUSER_CloseArchive(ncch_archive);
}
free(smdh_data);
amExit();
return res;
}
void themes_check_installed(void * void_arg) void themes_check_installed(void * void_arg)
{ {
Thread_Arg_s * arg = (Thread_Arg_s *)void_arg; Thread_Arg_s * arg = (Thread_Arg_s *)void_arg;

View File

@@ -1,6 +1,6 @@
/* /*
* This file is part of Anemone3DS * This file is part of Anemone3DS
* Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md * Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by