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
* 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))
* Dylan G. ([@helloman892](https://github.com/helloman892))
* Nils P. ([@ZetaDesigns](https://github.com/ZetaDesigns))
* Matt Kenny ([@KennLDN](https://github.com/KennLDN))
* Matt Kenny ([@mattkenster](https://github.com/mattkenster))
# Minor Contributors
* Nic ([@Wizzrobes](https://github.com/Wizzrobes))
@@ -15,5 +15,4 @@
* [@uyuiyu](https://github.com/uyuiyu)
* Guillaume Gérard ([@GreatWizard](https://github.com/GreatWizard))
* Joel ([@joel16](https://github.com/joel16))
* [@thedax](https://github.com/thedax)
* [@Wryyyong](https://github.com/Wryyyong)
* [@thedax](https://github.com/thedax)

View File

@@ -89,7 +89,7 @@ CFLAGS := -g -Wall -Wextra -O2 -mword-relocations \
-ffunction-sections \
$(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)),)
CFLAGS += -DCITRA_MODE
endif

View File

@@ -1,11 +1,13 @@
![# Anemone3DS](https://github.com/astronautlevel2/Anemone3DS/blob/master/meta/banner.png)
A Theme and Splashscreen Manager for the Nintendo 3DS, written in C.\
To-do list here: https://trello.com/b/F1YSa1VK
# Dependencies
* 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).
* 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
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
* 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
* it under the terms of the GNU General Public License as published by
@@ -30,24 +30,21 @@
#include "common.h"
typedef struct {
u16* camera_buffer;
Handle event_stop;
Thread cam_thread, ui_thread;
LightEvent event_cam_info, event_ui_info;
CondVar cond;
LightLock mut;
u32 num_readers_active;
bool writer_waiting;
bool writer_active;
bool any_update;
u16 *camera_buffer;
C2D_Image image;
C3D_Tex *tex;
Handle mutex;
volatile bool finished;
volatile bool success;
Handle cancel;
Handle started;
bool capturing;
struct quirc* context;
} qr_data;
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
* 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
* it under the terms of the GNU General Public License as published by
@@ -31,7 +31,7 @@
typedef u32 Color;
typedef enum {
enum {
COLOR_BACKGROUND, //silver-y black
COLOR_ACCENT,
COLOR_WHITE,

View File

@@ -1,6 +1,6 @@
/*
* 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
* 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
* 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
* 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_BGM,
INSTALL_DUMPING_THEME,
INSTALL_DUMPING_ALL_THEMES,
INSTALL_NONE,
} InstallType;
@@ -84,9 +81,6 @@ typedef enum {
TEXT_INSTALL_LOADING_REMOTE_PREVIEW,
TEXT_INSTALL_LOADING_REMOTE_BGM,
TEXT_INSTALL_DUMPING_THEME,
TEXT_INSTALL_DUMPING_ALL_THEMES,
// Other text
TEXT_VERSION,
@@ -131,7 +125,7 @@ typedef enum {
#define BUTTONS_INFO_LINES 4
#define BUTTONS_INFO_COLUNMNS 2
typedef enum {
enum {
BUTTONS_Y_INFO = BUTTONS_START_Y+5,
BUTTONS_Y_LINE_1 = BUTTONS_START_Y + BUTTONS_STEP*1,
@@ -162,7 +156,7 @@ void start_frame(void);
void end_frame(void);
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);
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_interface(Entry_List_s* list, Instructions_s instructions);
#endif
#endif

View File

@@ -1,6 +1,6 @@
/*
* 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
* it under the terms of the GNU General Public License as published by
@@ -29,11 +29,9 @@
#include "common.h"
#define ILLEGAL_CHARS "><\"?;:/\\+,.|[=]"
extern FS_Archive ArchiveSD;
extern FS_Archive ArchiveHomeExt;
extern FS_Archive ArchiveThemeExt;
FS_Archive ArchiveSD;
FS_Archive ArchiveHomeExt;
FS_Archive ArchiveThemeExt;
Result open_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 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 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);
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
* 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
* 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",
NULL
NULL,
},
{
"\uE004 Sorting menu",
"\uE005 Dumping menu"
NULL
},
{
"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
* 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
* 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);
u32 load_data(char * filename, Entry_s entry, char ** buf);
#endif
#endif

View File

@@ -1,6 +1,6 @@
/*
* 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
* it under the terms of the GNU General Public License as published by
@@ -34,7 +34,7 @@
#include <tremor/ivorbisfile.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 {
OggVorbis_File vf;
@@ -51,4 +51,4 @@ typedef struct {
void play_audio(audio_s *);
#endif
#endif

View File

@@ -1,6 +1,6 @@
/*
* 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
* it under the terms of the GNU General Public License as published by
@@ -29,9 +29,8 @@
#include "common.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_BASE_API_URL THEMEPLAZA_BASE_URL THEMEPLAZA_API_URL
@@ -52,6 +51,6 @@
#define CACHE_PATH_FORMAT "/3ds/" APP_TITLE "/cache/%" JSON_INTEGER_FORMAT
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

View File

@@ -1,6 +1,6 @@
/*
* 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
* 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
* 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
* it under the terms of the GNU General Public License as published by
@@ -49,9 +49,8 @@ typedef struct {
u8 _padding1[0x13b8];
ThemeEntry_s theme_entry;
ThemeEntry_s shuffle_themes[MAX_SHUFFLE_THEMES];
u8 shuffle_seedA[0xb];
u8 shuffle;
u8 shuffle_seedB[0xa];
u8 _padding2[0xb];
bool shuffle;
} SaveData_dat_s;
typedef struct {
@@ -76,9 +75,6 @@ Result bgm_install(Entry_s theme);
Result shuffle_install(Entry_List_s themes);
Result dump_current_theme(void);
Result dump_all_themes(void);
void themes_check_installed(void * void_arg);
#endif
#endif

View File

@@ -1,6 +1,6 @@
/*
* 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
* 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
* 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
* it under the terms of the GNU General Public License as published by
@@ -36,63 +36,28 @@
#include <archive.h>
#include <archive_entry.h>
static void start_read(qr_data *data)
void exit_qr(qr_data *data)
{
LightLock_Lock(&data->mut);
while(data->writer_waiting || data->writer_active)
{
CondVar_WaitTimeout(&data->cond, &data->mut, 1000000);
}
DEBUG("Exiting QR\n");
svcSignalEvent(data->cancel);
while(!data->finished)
svcSleepThread(1000000);
svcCloseHandle(data->cancel);
data->capturing = false;
AtomicIncrement(&data->num_readers_active);
LightLock_Unlock(&data->mut);
}
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);
free(data->camera_buffer);
free(data->tex);
quirc_destroy(data->context);
}
static void capture_cam_thread(void *arg)
void capture_cam_thread(void *arg)
{
qr_data *data = (qr_data *) arg;
Handle cam_events[3] = {0};
cam_events[0] = data->event_stop;
Handle events[3] = {0};
events[0] = data->cancel;
u32 transferUnit;
const u32 bufsz = 400 * 240 * sizeof(u16);
u16 *buffer = linearAlloc(bufsz);
u16 *buffer = calloc(1, 400 * 240 * sizeof(u16));
camInit();
CAMU_SetSize(SELECT_OUT1, SIZE_CTR_TOP_LCD, 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_SetAutoWhiteBalance(SELECT_OUT1, true);
CAMU_Activate(SELECT_OUT1);
CAMU_GetBufferErrorInterruptEvent(&cam_events[2], PORT_CAM1);
CAMU_GetBufferErrorInterruptEvent(&events[2], PORT_CAM1);
CAMU_SetTrimming(PORT_CAM1, false);
CAMU_GetMaxBytes(&transferUnit, 400, 240);
CAMU_SetTransferBytes(PORT_CAM1, transferUnit, 400, 240);
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);
svcSignalEvent(data->started);
bool cancel = false;
while (!cancel)
while (!cancel)
{
s32 index = 0;
svcWaitSynchronizationN(&index, cam_events, 3, false, U64_MAX);
svcWaitSynchronizationN(&index, events, 3, false, U64_MAX);
switch(index) {
case 0:
DEBUG("Cancel event received\n");
cancel = true;
break;
case 1:
svcCloseHandle(cam_events[1]);
cam_events[1] = 0;
start_write(data);
memcpy(data->camera_buffer, buffer, bufsz);
data->any_update = true;
stop_write(data);
CAMU_SetReceiving(&cam_events[1], buffer, PORT_CAM1, bufsz, transferUnit);
svcCloseHandle(events[1]);
events[1] = 0;
svcWaitSynchronization(data->mutex, U64_MAX);
memcpy(data->camera_buffer, buffer, 400 * 240 * sizeof(u16));
GSPGPU_FlushDataCache(data->camera_buffer, 400 * 240 * sizeof(u16));
svcReleaseMutex(data->mutex);
CAMU_SetReceiving(&events[1], buffer, PORT_CAM1, 400 * 240 * sizeof(u16), transferUnit);
break;
case 2:
svcCloseHandle(cam_events[1]);
cam_events[1] = 0;
svcCloseHandle(events[1]);
events[1] = 0;
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);
break;
default:
@@ -154,266 +115,213 @@ static void capture_cam_thread(void *arg)
CAMU_ClearBuffer(PORT_CAM1);
CAMU_Activate(SELECT_NONE);
camExit();
linearFree(buffer);
for(int i = 1; i < 3; i++)
{
if(cam_events[i] != 0) {
svcCloseHandle(cam_events[i]);
cam_events[i] = 0;
free(buffer);
for(int i = 0; i < 3; i++) {
if(events[i] != 0) {
svcCloseHandle(events[i]);
events[i] = 0;
}
}
LightEvent_Signal(&data->event_cam_info);
svcCloseHandle(data->mutex);
data->finished = true;
}
static void update_ui(void *arg)
void update_ui(void *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) };
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
while (!data->finished)
{
draw_base_interface();
// Untiled texture loading code adapted from FBI
start_read(data);
if(data->any_update)
{
for(u32 y = 0; y < 240; y++) {
const u32 srcPos = y * 400;
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)));
svcWaitSynchronization(data->mutex, U64_MAX);
for(u32 x = 0; x < 400 && !data->finished; x++) {
for(u32 y = 0; y < 256 && !data->finished; 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);
u32 srcPos = (y * 400 + x) * sizeof(u16);
((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);
draw_text_center(GFX_BOTTOM, 4, 0.5, 0.5, 0.5, colors[COLOR_WHITE], "Press \uE005 To Quit");
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)
{
throw_error("Capture cam thread creation failed\nPlease report this to the developers", ERROR_LEVEL_ERROR);
LightEvent_Signal(&data->event_cam_info);
LightEvent_Signal(&data->event_ui_info);
data->mutex = 0;
data->cancel = 0;
svcCreateEvent(&data->cancel, RESET_STICKY);
svcCreateMutex(&data->mutex, false);
if(threadCreate(capture_cam_thread, data, 0x10000, 0x1A, 1, true) == NULL)
return false;
}
if((data->ui_thread = threadCreate(update_ui, data, 0x10000, 0x1A, 1, false)) == NULL)
svcWaitSynchronization(data->started, U64_MAX);
if(threadCreate(update_ui, data, 0x10000, 0x1A, 1, true) == NULL)
{
LightEvent_Signal(&data->event_ui_info);
exit_qr(data);
return false;
}
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 h;
u8 *image = (u8*) quirc_begin(data->context, &w, &h);
start_read(data);
for (int y = 0; y < h; y++) {
const int actual_y = y * w;
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);
for (ssize_t x = 0; x < w; x++) {
for (ssize_t y = 0; y < h; y++) {
u16 px = data->camera_buffer[y * 400 + x];
image[y * w + x] = (u8)(((((px >> 11) & 0x1F) << 3) + (((px >> 5) & 0x3F) << 2) + ((px & 0x1F) << 3)) / 3);
}
}
stop_read(data);
quirc_end(data->context);
if(quirc_count(data->context) > 0)
{
struct quirc_code code;
struct quirc_data scan_data;
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)
{
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)
if(zip_size != 0)
{
EntryMode mode = MODE_AMOUNT;
draw_install(INSTALL_CHECKING_DOWNLOAD);
char * buf = NULL;
do {
if(zip_memory_to_buf("body_LZ.bin", zip_buf, zip_size, &buf) != 0)
{
mode = MODE_THEMES;
break;
}
struct archive *a = archive_read_new();
archive_read_support_format_zip(a);
free(buf);
buf = NULL;
if(zip_memory_to_buf("splash.bin", zip_buf, zip_size, &buf) != 0)
{
mode = MODE_SPLASHES;
break;
}
int r = archive_read_open_memory(a, zip_buf, zip_size);
archive_read_free(a);
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)
if(r == ARCHIVE_OK)
{
save_zip_to_sd(filename, zip_size, zip_buf, mode);
success = true;
EntryMode mode = MODE_AMOUNT;
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
{
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
{
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
* 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
* 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
* 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
* 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_PREVIEW], staticBuf, "Downloading preview, 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++)
C2D_TextOptimize(&text[i]);
@@ -276,7 +274,7 @@ void draw_base_interface(void)
set_screen(top);
}
void throw_error(const char* error, ErrorLevel level)
void throw_error(char* error, ErrorLevel level)
{
Text bottom_text = TEXT_AMOUNT;
Color text_color = COLOR_WHITE;
@@ -302,7 +300,7 @@ void throw_error(const char* error, ErrorLevel level)
draw_base_interface();
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();
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)
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)
{
char* last_space = NULL;
@@ -554,18 +552,13 @@ void draw_grid_interface(Entry_List_s* list, Instructions_s instructions)
vertical_offset += 24;
horizontal_offset += 16;
// theoretically impossible to have no icon when from the api
/*
if(!current_entry->placeholder_color)
{
*/
C2D_Image * image = list->icons[i];
C2D_DrawImageAt(*image, horizontal_offset, vertical_offset, 0.5f, NULL, 1.0f, 1.0f);
/*
}
else
C2D_DrawRectSolid(horizontal_offset, vertical_offset, 0.5f, list->entry_size, list->entry_size, current_entry->placeholder_color);
*/
if(i == selected_entry)
{

View File

@@ -1,6 +1,6 @@
/*
* 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
* it under the terms of the GNU General Public License as published by
@@ -25,30 +25,25 @@
*/
#include <strings.h>
#include "fs.h"
#include "draw.h"
#include "unicode.h"
#include <archive.h>
#include <archive_entry.h>
FS_Archive ArchiveSD;
FS_Archive ArchiveHomeExt;
FS_Archive ArchiveThemeExt;
Result open_archives(void)
{
romfsInit();
u8 regionCode;
u32 archive1;
u32 archive2;
Result res = 0;
FS_Path home;
FS_Path theme;
CFGU_SecureInfoGetRegion(&regionCode);
switch(regionCode)
{
@@ -68,7 +63,7 @@ Result open_archives(void)
archive1 = 0x00;
archive2 = 0x00;
}
if(R_FAILED(res = FSUSER_OpenArchive(&ArchiveSD, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")))) return res;
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/" APP_TITLE), FS_ATTRIBUTE_DIRECTORY);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/3ds/" APP_TITLE "/cache"), FS_ATTRIBUTE_DIRECTORY);
u32 homeMenuPath[3] = {MEDIATYPE_SD, archive2, 0};
home.type = PATH_BINARY;
home.size = 0xC;
home.data = homeMenuPath;
if(R_FAILED(res = FSUSER_OpenArchive(&ArchiveHomeExt, ARCHIVE_EXTDATA, home))) return res;
u32 themePath[3] = {MEDIATYPE_SD, archive1, 0};
theme.type = PATH_BINARY;
theme.size = 0xC;
theme.data = themePath;
if(R_FAILED(res = FSUSER_OpenArchive(&ArchiveThemeExt, ARCHIVE_EXTDATA, theme))) return res;
Handle test_handle;
if(R_FAILED(res = FSUSER_OpenFile(&test_handle, ArchiveThemeExt, fsMakePath(PATH_ASCII, "/ThemeManage.bin"), FS_OPEN_READ, 0))) return res;
FSFILE_Close(test_handle);
return 0;
}
Result close_archives(void)
{
Result 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(ArchiveThemeExt))) return res;
return 0;
}
u32 file_to_buf(FS_Path path, FS_Archive archive, char** buf)
{
Handle file;
Result res = 0;
if (R_FAILED(res = FSUSER_OpenFile(&file, archive, path, FS_OPEN_READ, 0))) return 0;
u64 size;
FSFILE_GetSize(file, &size);
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);
int r = archive_read_open_filename(a, path, 0x4000);
free(path);
if(r != ARCHIVE_OK)
{
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;
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)
{
Handle handle;
@@ -351,116 +203,4 @@ void remake_file(FS_Path path, FS_Archive archive, u32 size)
char *buf = calloc(size, 1);
buf_to_file(size, path, archive, 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
* 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
* 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)
{
if(icon == NULL)
{
memcpy(entry->name, fallback_name, 0x80);
@@ -93,12 +92,27 @@ void parse_smdh(Icon_s *icon, Entry_s * entry, const u16 * fallback_name)
return;
}
memcpy(entry->name, icon->name, 0x40*sizeof(u16));
memcpy(entry->desc, icon->desc, 0x80*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)
{
char *info_buffer = NULL;
@@ -106,9 +120,7 @@ static C2D_Image * load_entry_icon(Entry_s entry)
if(!size) return NULL;
Icon_s * smdh = (Icon_s *)info_buffer;
C2D_Image* out = loadTextureIcon(smdh);
free(info_buffer);
return out;
return loadTextureIcon(smdh);
}
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)
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;
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++;
Entry_s * new_list = realloc(list->entries, list->entries_count * sizeof(Entry_s));
if(new_list == NULL)
{
// out of memory: still allow use of currently loaded entries.
// Many things might die, depending on the heap layout after
list->entries_count--;
free(buf);
free(list->entries);
list->entries = NULL;
res = -1;
DEBUG("break\n");
break;
}
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]);
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");
parse_entry_smdh(current_entry, dir_entry.name);
}
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))
delta = -SIGN(delta) * (current_list->entries_count - abs(delta));
int absdelta = abs(delta);
int starti = current_list->scroll;
int endi = starti + abs(delta);
int endi = starti + absdelta;
if(delta < 0)
{
endi -= abs(delta) + 1;
starti += abs(delta) - 1;
endi -= absdelta + 1;
starti += absdelta - 1;
}
int ctr = 0;
Entry_s ** entries = calloc(abs(delta), sizeof(Entry_s *));
int * indexes = calloc(abs(delta), sizeof(int));
Entry_s ** entries = calloc(absdelta, sizeof(Entry_s *));
int * indexes = calloc(absdelta, sizeof(int));
bool released = false;
C2D_Image ** icons = current_list->icons;
@@ -393,36 +390,54 @@ static bool load_icons(Entry_List_s * current_list, Handle mutex)
indexes[ctr] = index;
}
#undef SIGN
if(abs(delta) < 4)
static int cutoff[] = {
0, // absdelta = 1
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);
released = true;
svcSleepThread(1e7);
}
svcSleepThread(1e7);
starti = 0;
endi = abs(delta);
for(int i = starti; i < endi; i++)
if(delta > 0)
{
starti = 0;
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];
int index = indexes[i];
C2D_Image * image = icons[index];
if (icons[index] != NULL)
{
C3D_TexDelete(image->tex);
free(image->tex);
free(image);
}
C3D_TexDelete(image->tex);
free(image->tex);
free(image);
icons[index] = load_entry_icon(*current_entry);
if(!released && i > endi/2)
if(!released && i == actual_cutoff)
{
svcReleaseMutex(mutex);
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 ||
color_type == PNG_COLOR_TYPE_GRAY ||
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 ||
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));
}
free(row_pointers[j]);
}
free(row_pointers);
*preview_offset = (width-400)/2;
return true;
@@ -652,4 +663,4 @@ Result load_audio(Entry_s entry, audio_s *audio)
DEBUG("<load_audio> fmemopen failed!\n");
return MAKERESULT(RL_FATAL, RS_NOTFOUND, RM_APPLICATION, RD_NOT_FOUND);
}
}
}

View File

@@ -1,6 +1,6 @@
/*
* 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
* 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++)
{
if (list->icons[i] == NULL) continue;
C3D_TexDelete(list->icons[i]->tex);
free(list->icons[i]->tex);
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)
{
(void)textlen;
int typed_value = atoi(text);
if(typed_value > *(int*)entries_count)
{
@@ -318,7 +316,7 @@ static void toggle_shuffle(Entry_List_s * list)
current_entry->no_bgm_shuffle = true;
}
}
else
else
{
current_entry->in_shuffle = true;
list->shuffle_count++;
@@ -360,6 +358,7 @@ int main(void)
bool preview_mode = false;
int preview_offset = 0;
bool qr_mode = false;
bool install_mode = false;
bool extra_mode = false;
C2D_Image preview = {0};
@@ -401,13 +400,14 @@ int main(void)
{
if(key_l)
index = 0;
else if(key_r) // uncomment when we use the right menu. we don't for now
index = 2;
// else if(key_r) // uncomment when we use the right menu. we don't for now
// index = 2;
}
instructions = extra_instructions[index];
}
if(preview_mode)
if(qr_mode) take_picture();
else if(preview_mode)
{
draw_preview(preview, preview_offset);
}
@@ -439,14 +439,14 @@ int main(void)
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:
current_mode++;
current_mode %= MODE_AMOUNT;
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:
draw_base_interface();
@@ -479,7 +479,7 @@ int main(void)
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:
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;
int selected_entry = current_list->selected_entry;
@@ -670,23 +670,6 @@ int main(void)
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;
}

View File

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

View File

@@ -14,7 +14,6 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <limits.h>
#include <string.h>
#include <stdlib.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 y = (c[3]*u + c[4]*v + c[5]) / den;
ret->x = (int) rint(x);
ret->y = (int) rint(y);
ret->x = rint(x);
ret->y = rint(y);
}
static void perspective_unmap(const double *c,
@@ -122,113 +121,234 @@ static void perspective_unmap(const double *c,
* Span-based floodfill routine
*/
#define FLOOD_FILL_MAX_DEPTH 128
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,
span_func_t func, void *user_data,
int depth)
#if 0 // recursive flood fill
#define FLOOD_FILL_MAX_DEPTH 4096
struct flood_fill_params{
struct quirc *q;
int from;
int to;
span_func_t func;
void *user_data;
};
static struct flood_fill_params ffp;
static void flood_fill_rec(int x, int y, int depth)
{
int left = x;
int right = x;
int i;
quirc_pixel_t *row = 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;
while (left > 0 && row[left - 1] == from)
while (left > 0 && row[left - 1] == ffp.from)
left--;
while (right < q->w - 1 && row[right + 1] == from)
while (right < ffp.q->w - 1 && row[right + 1] == ffp.from)
right++;
/* Fill the extent */
for (i = left; i <= right; i++)
row[i] = to;
row[i] = ffp.to;
if (func)
func(user_data, y, left, right);
if (ffp.func)
ffp.func(ffp.user_data, y, left, right);
/* Seed new flood-fills */
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++)
if (row[i] == from)
flood_fill_seed(q, i, y - 1, from, to,
func, user_data, depth + 1);
if (row[i] == ffp.from)
flood_fill_rec(i, y - 1, depth - 1);
}
if (y < q->h - 1) {
row = q->pixels + (y + 1) * q->w;
if (y < ffp.q->h - 1) {
row = ffp.q->pixels + (y + 1) * ffp.q->w;
for (i = left; i <= right; i++)
if (row[i] == from)
flood_fill_seed(q, i, y + 1, from, to,
func, user_data, depth + 1);
if (row[i] == ffp.from)
flood_fill_rec(i, y + 1, depth - 1);
}
}
static void flood_fill_seed(struct quirc *q, int x, int y, int from, int to,
span_func_t func, void *user_data)
{
ffp.q = q;
ffp.from = from;
ffp.to = to;
ffp.func = func;
ffp.user_data = user_data;
flood_fill_rec(x, y, FLOOD_FILL_MAX_DEPTH);
}
#else // stacked flood fill
#define FILL_STACK_CHUNK_SIZE 0x400
struct fill_stack_chunk{
int x[FILL_STACK_CHUNK_SIZE];
int y[FILL_STACK_CHUNK_SIZE];
struct fill_stack_chunk *prev;
};
struct fill_stack{
struct fill_stack_chunk *last_chunk;
int index;
};
static void fill_stack_init(struct fill_stack *s){
s->last_chunk = NULL;
}
static int fill_stack_is_empty(struct fill_stack *s){
return s->last_chunk == NULL;
}
static void fill_stack_push(struct fill_stack *s, int x, int y){
struct fill_stack_chunk *c;
if(s->last_chunk != NULL && s->index < FILL_STACK_CHUNK_SIZE - 1){
c = s->last_chunk;
s->index++;
}else{
c = (struct fill_stack_chunk*)malloc(sizeof(struct fill_stack_chunk));
if(c == NULL){
return;
}
c->prev = s->last_chunk;
s->last_chunk = c;
s->index = 0;
}
c->x[s->index] = x;
c->y[s->index] = y;
}
static void fill_stack_pop(struct fill_stack *s, int *px, int *py){
struct fill_stack_chunk *c = s->last_chunk;
if(c == NULL){
return;
}
*px = c->x[s->index];
*py = c->y[s->index];
if(s->index > 0){
s->index--;
}else{
s->last_chunk = c->prev;
s->index = FILL_STACK_CHUNK_SIZE - 1;
free(c);
}
}
static void flood_fill_seed(struct quirc *q, int start_x, int start_y, int from, int to,
span_func_t func, void *user_data)
{
struct fill_stack s;
fill_stack_init(&s);
fill_stack_push(&s, start_x, start_y);
do{
int x = 0, y = 0;
fill_stack_pop(&s, &x, &y);
int left = x, right = x, i;
quirc_pixel_t *row = q->pixels + y * q->w;
while (left > 0 && row[left - 1] == from)
left--;
while (right < q->w - 1 && row[right + 1] == from)
right++;
/* Fill the extent */
for (i = left; i <= right; i++)
row[i] = to;
if (func)
func(user_data, y, left, right);
/* Seed new flood-fills */
if (y > 0) {
row = q->pixels + (y - 1) * q->w;
for (i = left; i <= right; i++)
if (row[i] == from)
fill_stack_push(&s, i, y - 1);
}
if (y < q->h - 1) {
row = q->pixels + (y + 1) * q->w;
for (i = left; i <= right; i++)
if (row[i] == from)
fill_stack_push(&s, i, y + 1);
}
}while(!fill_stack_is_empty(&s));
}
#endif
/************************************************************************
* Adaptive thresholding
*/
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
const int HISTOGRAM_SIZE = 256;
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]++;
}
for (y = 0; y < q->h; y++) {
int row_average[q->w];
// Calculate weighted sum of histogram values
int sum = 0;
for (int i = 0; i < HISTOGRAM_SIZE; ++i) {
sum += i * histogram[i];
}
memset(row_average, 0, sizeof(row_average));
// Compute threshold
int sumB = 0;
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;
for (x = 0; x < q->w; x++) {
int w, u;
// Weighted foreground
const int q2 = numPixels - q1;
if (q2 == 0)
break;
if (y & 1) {
w = x;
u = q->w - 1 - x;
} else {
w = q->w - 1 - x;
u = x;
}
sumB += i * histogram[i];
const double m1 = (double)sumB / q1;
const double m2 = ((double)sum - sumB) / q2;
const double m1m2 = m1 - m2;
const double variance = m1m2 * m1m2 * q1 * q2;
if (variance >= max) {
threshold = i;
max = variance;
avg_w = (avg_w * (threshold_s - 1)) /
threshold_s + row[w];
avg_u = (avg_u * (threshold_s - 1)) /
threshold_s + row[u];
row_average[w] += avg_w;
row_average[u] += avg_u;
}
}
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)
{
(void)y;
(void)y;
((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->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;
}
@@ -331,7 +451,7 @@ static void find_region_corners(struct quirc *q,
psd.scores[0] = -1;
flood_fill_seed(q, region->seed.x, region->seed.y,
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.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,
QUIRC_PIXEL_BLACK, rcode,
find_other_corners, &psd, 0);
find_other_corners, &psd);
}
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;
int x;
int last_color = 0;
int last_color;
int run_length = 0;
int run_count = 0;
int pb[5];
@@ -768,7 +888,7 @@ static int fitness_all(const struct quirc *q, int index)
/* Check alignment patterns */
ap_count = 0;
while ((ap_count < QUIRC_MAX_ALIGNMENT) && info->apat[ap_count])
while (info->apat[ap_count])
ap_count++;
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];
int j;
int best = 0;
int best_score = INT_MAX;
int best_score = 0;
for (j = 0; j < 4; 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,
qr->align_region, QUIRC_PIXEL_BLACK,
NULL, NULL, 0);
NULL, NULL);
flood_fill_seed(q, reg->seed.x, reg->seed.y,
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);
}
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;
}
uint8_t* source = q->image;
quirc_pixel_t* dest = q->pixels;
int length = q->w * q->h;
while (length--) {
uint8_t value = *source++;
*dest++ = (value < threshold) ? QUIRC_PIXEL_BLACK : QUIRC_PIXEL_WHITE;
} else {
int x, y;
for (y = 0; y < q->h; y++) {
for (x = 0; x < q->w; x++) {
q->pixels[y * q->w + x] = q->image[y * q->w + x];
}
}
}
}
@@ -1104,8 +1223,8 @@ void quirc_end(struct quirc *q)
{
int i;
uint8_t threshold = otsu(q);
pixels_setup(q, threshold);
pixels_setup(q);
threshold(q);
for (i = 0; i < q->h; i++)
finder_scan(q, i);

View File

@@ -36,73 +36,34 @@ struct quirc *quirc_new(void)
void quirc_destroy(struct quirc *q)
{
free(q->image);
/* q->pixels may alias q->image when their type representation is of the
same size, so we need to be careful here to avoid a double free */
if (!QUIRC_PIXEL_ALIAS_IMAGE)
if (q->image)
free(q->image);
if (sizeof(*q->image) != sizeof(*q->pixels))
free(q->pixels);
free(q);
}
int quirc_resize(struct quirc *q, int w, int h)
{
uint8_t *image = NULL;
quirc_pixel_t *pixels = NULL;
uint8_t *new_image = realloc(q->image, w * h);
/*
* XXX: w and h should be size_t (or at least unsigned) as negatives
* 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 (!new_image)
return -1;
/*
* alloc a new buffer for q->image. We avoid realloc(3) because we want
* on failure to be leave `q` in a consistant, unmodified state.
*/
image = calloc(w, h);
if (!image)
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;
if (sizeof(*q->image) != sizeof(*q->pixels)) {
size_t new_size = w * h * sizeof(quirc_pixel_t);
quirc_pixel_t *new_pixels = realloc(q->pixels, new_size);
if (!new_pixels)
return -1;
q->pixels = new_pixels;
}
/* alloc succeeded, update `q` with the new size and buffers */
q->image = new_image;
q->w = w;
q->h = h;
free(q->image);
q->image = image;
if (!QUIRC_PIXEL_ALIAS_IMAGE) {
free(q->pixels);
q->pixels = pixels;
}
return 0;
/* NOTREACHED */
fail:
free(image);
free(pixels);
return -1;
}
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)
{
// note from Anemone3DS dev - L88 used to compare err >= 0, but err is always positive
if (err < sizeof(error_table) / sizeof(error_table[0]))
return error_table[err];

View File

@@ -19,10 +19,6 @@
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
struct quirc;
/* Obtain the library version string. */
@@ -121,7 +117,7 @@ struct quirc_code {
* is a bitmask giving the actual values of cells. If the cell
* at (x, y) is black, then the following bit is set:
*
* cell_bitmap[i >> 3] & (1 << (i & 7))
* cell_bitmap[i << 3] & (1 << (i & 7))
*
* 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,
struct quirc_data *data);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -32,10 +32,8 @@
#define QUIRC_PERSPECTIVE_PARAMS 8
#if QUIRC_MAX_REGIONS < UINT8_MAX
#define QUIRC_PIXEL_ALIAS_IMAGE 1
typedef uint8_t quirc_pixel_t;
#elif QUIRC_MAX_REGIONS < UINT16_MAX
#define QUIRC_PIXEL_ALIAS_IMAGE 0
typedef uint16_t quirc_pixel_t;
#else
#error "QUIRC_MAX_REGIONS > 65534 is not supported"
@@ -100,9 +98,9 @@ struct quirc {
#define QUIRC_MAX_ALIGNMENT 7
struct quirc_rs_params {
int bs; /* Small block size */
int dw; /* Small data words */
int ns; /* Number of small blocks */
int bs; /* Block size */
int dw; /* Data words */
int ce; /* Correctable errors */
};
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,
.apat = {0},
.ecc = {
{.bs = 26, .dw = 16, .ns = 1},
{.bs = 26, .dw = 19, .ns = 1},
{.bs = 26, .dw = 9, .ns = 1},
{.bs = 26, .dw = 13, .ns = 1}
{.bs = 26, .dw = 16, .ce = 4},
{.bs = 26, .dw = 19, .ce = 2},
{.bs = 26, .dw = 9, .ce = 8},
{.bs = 26, .dw = 13, .ce = 6}
}
},
{ /* Version 2 */
.data_bytes = 44,
.apat = {6, 18, 0},
.ecc = {
{.bs = 44, .dw = 28, .ns = 1},
{.bs = 44, .dw = 34, .ns = 1},
{.bs = 44, .dw = 16, .ns = 1},
{.bs = 44, .dw = 22, .ns = 1}
{.bs = 44, .dw = 28, .ce = 8},
{.bs = 44, .dw = 34, .ce = 4},
{.bs = 44, .dw = 16, .ce = 14},
{.bs = 44, .dw = 22, .ce = 11}
}
},
{ /* Version 3 */
.data_bytes = 70,
.apat = {6, 22, 0},
.ecc = {
{.bs = 70, .dw = 44, .ns = 1},
{.bs = 70, .dw = 55, .ns = 1},
{.bs = 35, .dw = 13, .ns = 2},
{.bs = 35, .dw = 17, .ns = 2}
{.bs = 70, .dw = 44, .ce = 13},
{.bs = 70, .dw = 55, .ce = 7},
{.bs = 35, .dw = 13, .ce = 11},
{.bs = 35, .dw = 17, .ce = 9}
}
},
{ /* Version 4 */
.data_bytes = 100,
.apat = {6, 26, 0},
.ecc = {
{.bs = 50, .dw = 32, .ns = 2},
{.bs = 100, .dw = 80, .ns = 1},
{.bs = 25, .dw = 9, .ns = 4},
{.bs = 50, .dw = 24, .ns = 2}
{.bs = 50, .dw = 32, .ce = 9},
{.bs = 100, .dw = 80, .ce = 10},
{.bs = 25, .dw = 9, .ce = 8},
{.bs = 50, .dw = 24, .ce = 13}
}
},
{ /* Version 5 */
.data_bytes = 134,
.apat = {6, 30, 0},
.ecc = {
{.bs = 67, .dw = 43, .ns = 2},
{.bs = 134, .dw = 108, .ns = 1},
{.bs = 33, .dw = 11, .ns = 2},
{.bs = 33, .dw = 15, .ns = 2}
{.bs = 67, .dw = 43, .ce = 12},
{.bs = 134, .dw = 108, .ce = 13},
{.bs = 33, .dw = 11, .ce = 11},
{.bs = 33, .dw = 15, .ce = 9}
}
},
{ /* Version 6 */
.data_bytes = 172,
.apat = {6, 34, 0},
.ecc = {
{.bs = 43, .dw = 27, .ns = 4},
{.bs = 86, .dw = 68, .ns = 2},
{.bs = 43, .dw = 15, .ns = 4},
{.bs = 43, .dw = 19, .ns = 4}
{.bs = 43, .dw = 27, .ce = 8},
{.bs = 86, .dw = 68, .ce = 9},
{.bs = 43, .dw = 15, .ce = 14},
{.bs = 43, .dw = 19, .ce = 12}
}
},
{ /* Version 7 */
.data_bytes = 196,
.apat = {6, 22, 38, 0},
.ecc = {
{.bs = 49, .dw = 31, .ns = 4},
{.bs = 98, .dw = 78, .ns = 2},
{.bs = 39, .dw = 13, .ns = 4},
{.bs = 32, .dw = 14, .ns = 2}
{.bs = 49, .dw = 31, .ce = 9},
{.bs = 98, .dw = 78, .ce = 10},
{.bs = 39, .dw = 13, .ce = 13},
{.bs = 32, .dw = 14, .ce = 9}
}
},
{ /* Version 8 */
.data_bytes = 242,
.apat = {6, 24, 42, 0},
.ecc = {
{.bs = 60, .dw = 38, .ns = 2},
{.bs = 121, .dw = 97, .ns = 2},
{.bs = 40, .dw = 14, .ns = 4},
{.bs = 40, .dw = 18, .ns = 4}
{.bs = 60, .dw = 38, .ce = 11},
{.bs = 121, .dw = 97, .ce = 12},
{.bs = 40, .dw = 14, .ce = 13},
{.bs = 40, .dw = 18, .ce = 11}
}
},
{ /* Version 9 */
.data_bytes = 292,
.apat = {6, 26, 46, 0},
.ecc = {
{.bs = 58, .dw = 36, .ns = 3},
{.bs = 146, .dw = 116, .ns = 2},
{.bs = 36, .dw = 12, .ns = 4},
{.bs = 36, .dw = 16, .ns = 4}
{.bs = 58, .dw = 36, .ce = 11},
{.bs = 146, .dw = 116, .ce = 15},
{.bs = 36, .dw = 12, .ce = 12},
{.bs = 36, .dw = 16, .ce = 10}
}
},
{ /* Version 10 */
.data_bytes = 346,
.apat = {6, 28, 50, 0},
.ecc = {
{.bs = 69, .dw = 43, .ns = 4},
{.bs = 86, .dw = 68, .ns = 2},
{.bs = 43, .dw = 15, .ns = 6},
{.bs = 43, .dw = 19, .ns = 6}
{.bs = 69, .dw = 43, .ce = 13},
{.bs = 86, .dw = 68, .ce = 9},
{.bs = 43, .dw = 15, .ce = 14},
{.bs = 43, .dw = 19, .ce = 12}
}
},
{ /* Version 11 */
.data_bytes = 404,
.apat = {6, 30, 54, 0},
.ecc = {
{.bs = 80, .dw = 50, .ns = 1},
{.bs = 101, .dw = 81, .ns = 4},
{.bs = 36, .dw = 12, .ns = 3},
{.bs = 50, .dw = 22, .ns = 4}
{.bs = 80, .dw = 50, .ce = 15},
{.bs = 101, .dw = 81, .ce = 10},
{.bs = 36, .dw = 12, .ce = 12},
{.bs = 50, .dw = 22, .ce = 14}
}
},
{ /* Version 12 */
.data_bytes = 466,
.apat = {6, 32, 58, 0},
.ecc = {
{.bs = 58, .dw = 36, .ns = 6},
{.bs = 116, .dw = 92, .ns = 2},
{.bs = 42, .dw = 14, .ns = 7},
{.bs = 46, .dw = 20, .ns = 4}
{.bs = 58, .dw = 36, .ce = 11},
{.bs = 116, .dw = 92, .ce = 12},
{.bs = 42, .dw = 14, .ce = 14},
{.bs = 46, .dw = 20, .ce = 14}
}
},
{ /* Version 13 */
.data_bytes = 532,
.apat = {6, 34, 62, 0},
.ecc = {
{.bs = 59, .dw = 37, .ns = 8},
{.bs = 133, .dw = 107, .ns = 4},
{.bs = 33, .dw = 11, .ns = 12},
{.bs = 44, .dw = 20, .ns = 8}
{.bs = 59, .dw = 37, .ce = 11},
{.bs = 133, .dw = 107, .ce = 13},
{.bs = 33, .dw = 11, .ce = 11},
{.bs = 44, .dw = 20, .ce = 12}
}
},
{ /* Version 14 */
.data_bytes = 581,
.apat = {6, 26, 46, 66, 0},
.ecc = {
{.bs = 64, .dw = 40, .ns = 4},
{.bs = 145, .dw = 115, .ns = 3},
{.bs = 36, .dw = 12, .ns = 11},
{.bs = 36, .dw = 16, .ns = 11}
{.bs = 65, .dw = 41, .ce = 12},
{.bs = 109, .dw = 87, .ce = 11},
{.bs = 36, .dw = 12, .ce = 12},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 15 */
.data_bytes = 655,
.apat = {6, 26, 48, 70, 0},
.ecc = {
{.bs = 65, .dw = 41, .ns = 5},
{.bs = 109, .dw = 87, .ns = 5},
{.bs = 36, .dw = 12, .ns = 11},
{.bs = 54, .dw = 24, .ns = 5}
{.bs = 65, .dw = 41, .ce = 12},
{.bs = 109, .dw = 87, .ce = 11},
{.bs = 36, .dw = 12, .ce = 12},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 16 */
.data_bytes = 733,
.apat = {6, 26, 50, 74, 0},
.ecc = {
{.bs = 73, .dw = 45, .ns = 7},
{.bs = 122, .dw = 98, .ns = 5},
{.bs = 45, .dw = 15, .ns = 3},
{.bs = 43, .dw = 19, .ns = 15}
{.bs = 73, .dw = 45, .ce = 14},
{.bs = 122, .dw = 98, .ce = 12},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 43, .dw = 19, .ce = 12}
}
},
{ /* Version 17 */
.data_bytes = 815,
.apat = {6, 30, 54, 78, 0},
.ecc = {
{.bs = 74, .dw = 46, .ns = 10},
{.bs = 135, .dw = 107, .ns = 1},
{.bs = 42, .dw = 14, .ns = 2},
{.bs = 50, .dw = 22, .ns = 1}
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 135, .dw = 107, .ce = 14},
{.bs = 42, .dw = 14, .ce = 14},
{.bs = 50, .dw = 22, .ce = 14}
}
},
{ /* Version 18 */
.data_bytes = 901,
.apat = {6, 30, 56, 82, 0},
.ecc = {
{.bs = 69, .dw = 43, .ns = 9},
{.bs = 150, .dw = 120, .ns = 5},
{.bs = 42, .dw = 14, .ns = 2},
{.bs = 50, .dw = 22, .ns = 17}
{.bs = 69, .dw = 43, .ce = 13},
{.bs = 150, .dw = 120, .ce = 15},
{.bs = 42, .dw = 14, .ce = 14},
{.bs = 50, .dw = 22, .ce = 14}
}
},
{ /* Version 19 */
.data_bytes = 991,
.apat = {6, 30, 58, 86, 0},
.ecc = {
{.bs = 70, .dw = 44, .ns = 3},
{.bs = 141, .dw = 113, .ns = 3},
{.bs = 39, .dw = 13, .ns = 9},
{.bs = 47, .dw = 21, .ns = 17}
{.bs = 70, .dw = 44, .ce = 13},
{.bs = 141, .dw = 113, .ce = 14},
{.bs = 39, .dw = 13, .ce = 13},
{.bs = 47, .dw = 21, .ce = 13}
}
},
{ /* Version 20 */
.data_bytes = 1085,
.apat = {6, 34, 62, 90, 0},
.ecc = {
{.bs = 67, .dw = 41, .ns = 3},
{.bs = 135, .dw = 107, .ns = 3},
{.bs = 43, .dw = 15, .ns = 15},
{.bs = 54, .dw = 24, .ns = 15}
{.bs = 67, .dw = 41, .ce = 13},
{.bs = 135, .dw = 107, .ce = 14},
{.bs = 43, .dw = 15, .ce = 14},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 21 */
.data_bytes = 1156,
.apat = {6, 28, 50, 72, 92, 0},
.ecc = {
{.bs = 68, .dw = 42, .ns = 17},
{.bs = 144, .dw = 116, .ns = 4},
{.bs = 46, .dw = 16, .ns = 19},
{.bs = 50, .dw = 22, .ns = 17}
{.bs = 68, .dw = 42, .ce = 13},
{.bs = 144, .dw = 116, .ce = 14},
{.bs = 46, .dw = 16, .ce = 15},
{.bs = 50, .dw = 22, .ce = 14}
}
},
{ /* Version 22 */
.data_bytes = 1258,
.apat = {6, 26, 50, 74, 98, 0},
.ecc = {
{.bs = 74, .dw = 46, .ns = 17},
{.bs = 139, .dw = 111, .ns = 2},
{.bs = 37, .dw = 13, .ns = 34},
{.bs = 54, .dw = 24, .ns = 7}
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 139, .dw = 111, .ce = 14},
{.bs = 37, .dw = 13, .ce = 12},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 23 */
.data_bytes = 1364,
.apat = {6, 30, 54, 78, 102, 0},
.ecc = {
{.bs = 75, .dw = 47, .ns = 4},
{.bs = 151, .dw = 121, .ns = 4},
{.bs = 45, .dw = 15, .ns = 16},
{.bs = 54, .dw = 24, .ns = 11}
{.bs = 75, .dw = 47, .ce = 14},
{.bs = 151, .dw = 121, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 24 */
.data_bytes = 1474,
.apat = {6, 28, 54, 80, 106, 0},
.ecc = {
{.bs = 73, .dw = 45, .ns = 6},
{.bs = 147, .dw = 117, .ns = 6},
{.bs = 46, .dw = 16, .ns = 30},
{.bs = 54, .dw = 24, .ns = 11}
{.bs = 73, .dw = 45, .ce = 14},
{.bs = 147, .dw = 117, .ce = 15},
{.bs = 46, .dw = 16, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 25 */
.data_bytes = 1588,
.apat = {6, 32, 58, 84, 110, 0},
.ecc = {
{.bs = 75, .dw = 47, .ns = 8},
{.bs = 132, .dw = 106, .ns = 8},
{.bs = 45, .dw = 15, .ns = 22},
{.bs = 54, .dw = 24, .ns = 7}
{.bs = 75, .dw = 47, .ce = 14},
{.bs = 132, .dw = 106, .ce = 13},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 26 */
.data_bytes = 1706,
.apat = {6, 30, 58, 86, 114, 0},
.ecc = {
{.bs = 74, .dw = 46, .ns = 19},
{.bs = 142, .dw = 114, .ns = 10},
{.bs = 46, .dw = 16, .ns = 33},
{.bs = 50, .dw = 22, .ns = 28}
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 142, .dw = 114, .ce = 14},
{.bs = 46, .dw = 16, .ce = 15},
{.bs = 50, .dw = 22, .ce = 14}
}
},
{ /* Version 27 */
.data_bytes = 1828,
.apat = {6, 34, 62, 90, 118, 0},
.ecc = {
{.bs = 73, .dw = 45, .ns = 22},
{.bs = 152, .dw = 122, .ns = 8},
{.bs = 45, .dw = 15, .ns = 12},
{.bs = 53, .dw = 23, .ns = 8}
{.bs = 73, .dw = 45, .ce = 14},
{.bs = 152, .dw = 122, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 53, .dw = 23, .ce = 15}
}
},
{ /* Version 28 */
.data_bytes = 1921,
.apat = {6, 26, 50, 74, 98, 122, 0},
.ecc = {
{.bs = 73, .dw = 45, .ns = 3},
{.bs = 147, .dw = 117, .ns = 3},
{.bs = 45, .dw = 15, .ns = 11},
{.bs = 54, .dw = 24, .ns = 4}
{.bs = 73, .dw = 45, .ce = 14},
{.bs = 147, .dw = 117, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 29 */
.data_bytes = 2051,
.apat = {6, 30, 54, 78, 102, 126, 0},
.ecc = {
{.bs = 73, .dw = 45, .ns = 21},
{.bs = 146, .dw = 116, .ns = 7},
{.bs = 45, .dw = 15, .ns = 19},
{.bs = 53, .dw = 23, .ns = 1}
{.bs = 73, .dw = 45, .ce = 14},
{.bs = 146, .dw = 116, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 73, .dw = 45, .ce = 14}
}
},
{ /* Version 30 */
.data_bytes = 2185,
.apat = {6, 26, 52, 78, 104, 130, 0},
.ecc = {
{.bs = 75, .dw = 47, .ns = 19},
{.bs = 145, .dw = 115, .ns = 5},
{.bs = 45, .dw = 15, .ns = 23},
{.bs = 54, .dw = 24, .ns = 15}
{.bs = 75, .dw = 47, .ce = 14},
{.bs = 145, .dw = 115, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 31 */
.data_bytes = 2323,
.apat = {6, 30, 56, 82, 108, 134, 0},
.ecc = {
{.bs = 74, .dw = 46, .ns = 2},
{.bs = 145, .dw = 115, .ns = 13},
{.bs = 45, .dw = 15, .ns = 23},
{.bs = 54, .dw = 24, .ns = 42}
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 145, .dw = 115, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 32 */
.data_bytes = 2465,
.apat = {6, 34, 60, 86, 112, 138, 0},
.ecc = {
{.bs = 74, .dw = 46, .ns = 10},
{.bs = 145, .dw = 115, .ns = 17},
{.bs = 45, .dw = 15, .ns = 19},
{.bs = 54, .dw = 24, .ns = 10}
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 145, .dw = 115, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 33 */
.data_bytes = 2611,
.apat = {6, 30, 58, 86, 114, 142, 0},
.apat = {6, 30, 58, 96, 114, 142, 0},
.ecc = {
{.bs = 74, .dw = 46, .ns = 14},
{.bs = 145, .dw = 115, .ns = 17},
{.bs = 45, .dw = 15, .ns = 11},
{.bs = 54, .dw = 24, .ns = 29}
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 145, .dw = 115, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 34 */
.data_bytes = 2761,
.apat = {6, 34, 62, 90, 118, 146, 0},
.ecc = {
{.bs = 74, .dw = 46, .ns = 14},
{.bs = 145, .dw = 115, .ns = 13},
{.bs = 46, .dw = 16, .ns = 59},
{.bs = 54, .dw = 24, .ns = 44}
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 145, .dw = 115, .ce = 15},
{.bs = 46, .dw = 16, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 35 */
.data_bytes = 2876,
.apat = {6, 30, 54, 78, 102, 126, 150},
.ecc = {
{.bs = 75, .dw = 47, .ns = 12},
{.bs = 151, .dw = 121, .ns = 12},
{.bs = 45, .dw = 15, .ns = 22},
{.bs = 54, .dw = 24, .ns = 39}
{.bs = 75, .dw = 47, .ce = 14},
{.bs = 151, .dw = 121, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 36 */
.data_bytes = 3034,
.apat = {6, 24, 50, 76, 102, 128, 154},
.ecc = {
{.bs = 75, .dw = 47, .ns = 6},
{.bs = 151, .dw = 121, .ns = 6},
{.bs = 45, .dw = 15, .ns = 2},
{.bs = 54, .dw = 24, .ns = 46}
{.bs = 75, .dw = 47, .ce = 14},
{.bs = 151, .dw = 121, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 37 */
.data_bytes = 3196,
.apat = {6, 28, 54, 80, 106, 132, 158},
.ecc = {
{.bs = 74, .dw = 46, .ns = 29},
{.bs = 152, .dw = 122, .ns = 17},
{.bs = 45, .dw = 15, .ns = 24},
{.bs = 54, .dw = 24, .ns = 49}
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 152, .dw = 122, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 38 */
.data_bytes = 3362,
.apat = {6, 32, 58, 84, 110, 136, 162},
.ecc = {
{.bs = 74, .dw = 46, .ns = 13},
{.bs = 152, .dw = 122, .ns = 4},
{.bs = 45, .dw = 15, .ns = 42},
{.bs = 54, .dw = 24, .ns = 48}
{.bs = 74, .dw = 46, .ce = 14},
{.bs = 152, .dw = 122, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 39 */
.data_bytes = 3532,
.apat = {6, 26, 54, 82, 110, 138, 166},
.ecc = {
{.bs = 75, .dw = 47, .ns = 40},
{.bs = 147, .dw = 117, .ns = 20},
{.bs = 45, .dw = 15, .ns = 10},
{.bs = 54, .dw = 24, .ns = 43}
{.bs = 75, .dw = 47, .ce = 14},
{.bs = 147, .dw = 117, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.bs = 54, .dw = 24, .ce = 15}
}
},
{ /* Version 40 */
.data_bytes = 3706,
.apat = {6, 30, 58, 86, 114, 142, 170},
.ecc = {
{.bs = 75, .dw = 47, .ns = 18},
{.bs = 148, .dw = 118, .ns = 19},
{.bs = 45, .dw = 15, .ns = 20},
{.bs = 54, .dw = 24, .ns = 34}
{.bs = 75, .dw = 47, .ce = 14},
{.bs = 148, .dw = 118, .ce = 15},
{.bs = 45, .dw = 15, .ce = 15},
{.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
* 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
* 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
* 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
* 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)
{
music_size = load_data("/bgm.bcstm", current_theme, &music);
if (music_size > BGM_MAX_SIZE)
if(music_size > BGM_MAX_SIZE)
{
free(music);
DEBUG("bgm too big\n");
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);
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);
}
remake_file(fsMakePath(PATH_ASCII, "/BgmCache.bin"), ArchiveThemeExt, BGM_MAX_SIZE);
res = buf_to_file(music_size, fsMakePath(PATH_ASCII, "/BgmCache.bin"), ArchiveThemeExt, music);
free(music);
if(R_FAILED(res)) return res;
} 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;
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;
memset(savedata->shuffle_themes, 0, sizeof(ThemeEntry_s)*MAX_SHUFFLE_THEMES);
savedata->shuffle = (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++)
{
savedata->shuffle_themes[i].type = 3;
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);
@@ -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);
}
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)
{
Thread_Arg_s * arg = (Thread_Arg_s *)void_arg;

View File

@@ -1,6 +1,6 @@
/*
* 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
* it under the terms of the GNU General Public License as published by