Compare commits
63 Commits
fix-scroll
...
basename
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc21e4d24b | ||
|
|
9a51667546 | ||
|
|
b81a9aaa4c | ||
|
|
6ba1ef111e | ||
|
|
0500b24431 | ||
|
|
83071d3734 | ||
|
|
4e2bea53c1 | ||
|
|
0da2594251 | ||
|
|
c5dc7448e4 | ||
|
|
999b764c26 | ||
|
|
18cb5c616f | ||
|
|
3f2e4c03f3 | ||
|
|
163e12d38a | ||
| 02c3e617ae | |||
|
|
6161874d07 | ||
|
|
cea9b8655a | ||
|
|
7745530764 | ||
|
|
0bab0f6700 | ||
|
|
38ab370cf1 | ||
|
|
5cea6c8df4 | ||
|
|
e4e0118c1a | ||
|
|
accdaaed2a | ||
|
|
3fb90e8197 | ||
|
|
aba2cd5f18 | ||
|
|
e5ea18e81a | ||
|
|
4720a49923 | ||
|
|
7871225999 | ||
|
|
1e5d09e85a | ||
|
|
a647494306 | ||
|
|
c801e28523 | ||
|
|
c9e1420a00 | ||
|
|
bc2f4ec581 | ||
|
|
9592d76c2c | ||
|
|
a266203a92 | ||
|
|
17afdbaae2 | ||
|
|
f6d7446eba | ||
|
|
c49129d408 | ||
|
|
f9a2ae5190 | ||
|
|
573b7d35e6 | ||
| a2b6eb94e4 | |||
|
|
95ff2dd3ba | ||
|
|
1c2e562dd6 | ||
|
|
9cb13f6be1 | ||
| 6a51b4eae5 | |||
| 14d9e99b7d | |||
| 36e8104dbb | |||
| 6bafa6a22e | |||
|
|
408b2903f2 | ||
| fbca873f52 | |||
| d1a821edf5 | |||
| a4532b0f3d | |||
|
|
f819f9d452 | ||
| 2c2c7f4b33 | |||
| b75d1acb21 | |||
| e8d0a2eb27 | |||
| f4217888ce | |||
|
|
d358dd134e | ||
|
|
ca9da9d297 | ||
|
|
71a4cde22e | ||
|
|
f7404cf162 | ||
|
|
ef774ce0b4 | ||
|
|
193feedf25 | ||
|
|
ddfc39db56 |
Binary file not shown.
BIN
.flask/icon.t3x
BIN
.flask/icon.t3x
Binary file not shown.
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"user": "astronautlevel2",
|
||||
"name": "Anemone3DS",
|
||||
"description": "A theme and boot splash manager for the Nintendo 3DS console"
|
||||
}
|
||||
39
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
39
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
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!**
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
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.
|
||||
@@ -1,10 +1,10 @@
|
||||
# Main Contributors
|
||||
* Alex Taber ([@astronautlevel2](https://github.com/astronautlevel2))
|
||||
* [@LiquidFenrir](https://github.com/LiquidFenrir)
|
||||
* Théo B. ([@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 ([@mattkenster](https://github.com/mattkenster))
|
||||
* Matt Kenny ([@KennLDN](https://github.com/KennLDN))
|
||||
|
||||
# Minor Contributors
|
||||
* Nic ([@Wizzrobes](https://github.com/Wizzrobes))
|
||||
@@ -15,4 +15,5 @@
|
||||
* [@uyuiyu](https://github.com/uyuiyu)
|
||||
* Guillaume Gérard ([@GreatWizard](https://github.com/GreatWizard))
|
||||
* Joel ([@joel16](https://github.com/joel16))
|
||||
* [@thedax](https://github.com/thedax)
|
||||
* [@thedax](https://github.com/thedax)
|
||||
* [@Wryyyong](https://github.com/Wryyyong)
|
||||
|
||||
2
Makefile
2
Makefile
@@ -89,7 +89,7 @@ CFLAGS := -g -Wall -Wextra -O2 -mword-relocations \
|
||||
-ffunction-sections \
|
||||
$(ARCH)
|
||||
|
||||
CFLAGS += $(INCLUDE) -DARM11 -D_3DS -D_GNU_SOURCE -DVERSION="\"$(VERSION)\"" -DUSER_AGENT="\"$(APP_TITLE)/$(VERSION)\"" -DAPP_TITLE="\"$(APP_TITLE)\""
|
||||
CFLAGS += $(INCLUDE) -D__3DS__ -D_GNU_SOURCE -DVERSION="\"$(VERSION)\"" -DUSER_AGENT="\"$(APP_TITLE)/$(VERSION)\"" -DAPP_TITLE="\"$(APP_TITLE)\""
|
||||
ifneq ($(strip $(CITRA_MODE)),)
|
||||
CFLAGS += -DCITRA_MODE
|
||||
endif
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||

|
||||
|
||||
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 64-bit Windows binary of makerom is available [here](https://hm892.s-ul.eu/U0Irkqih).
|
||||
* 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.
|
||||
|
||||
# Building
|
||||
First of all, make sure devkitARM is properly installed - `$DEVKITPRO` and `$DEVKITARM` should be set to `/opt/devkitpro` and `$DEVKITPRO/devkitARM`, respectively.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Anemone3DS
|
||||
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
|
||||
* Copyright (C) 2016-2020 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,21 +30,24 @@
|
||||
#include "common.h"
|
||||
|
||||
typedef struct {
|
||||
u16 *camera_buffer;
|
||||
C2D_Image image;
|
||||
C3D_Tex *tex;
|
||||
Handle mutex;
|
||||
volatile bool finished;
|
||||
volatile bool success;
|
||||
Handle cancel;
|
||||
Handle started;
|
||||
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;
|
||||
|
||||
bool capturing;
|
||||
struct quirc* context;
|
||||
} qr_data;
|
||||
|
||||
bool init_qr(void);
|
||||
void exit_qr(qr_data *data);
|
||||
void take_picture(void);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Anemone3DS
|
||||
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
|
||||
* Copyright (C) 2016-2020 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;
|
||||
|
||||
enum {
|
||||
typedef enum {
|
||||
COLOR_BACKGROUND, //silver-y black
|
||||
COLOR_ACCENT,
|
||||
COLOR_WHITE,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Anemone3DS
|
||||
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
|
||||
* Copyright (C) 2016-2020 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Anemone3DS
|
||||
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
|
||||
* Copyright (C) 2016-2020 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,6 +55,9 @@ typedef enum {
|
||||
INSTALL_LOADING_REMOTE_PREVIEW,
|
||||
INSTALL_LOADING_REMOTE_BGM,
|
||||
|
||||
INSTALL_DUMPING_THEME,
|
||||
INSTALL_DUMPING_ALL_THEMES,
|
||||
|
||||
INSTALL_NONE,
|
||||
} InstallType;
|
||||
|
||||
@@ -81,6 +84,9 @@ 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,
|
||||
|
||||
@@ -125,7 +131,7 @@ typedef enum {
|
||||
#define BUTTONS_INFO_LINES 4
|
||||
#define BUTTONS_INFO_COLUNMNS 2
|
||||
|
||||
enum {
|
||||
typedef enum {
|
||||
BUTTONS_Y_INFO = BUTTONS_START_Y+5,
|
||||
|
||||
BUTTONS_Y_LINE_1 = BUTTONS_START_Y + BUTTONS_STEP*1,
|
||||
@@ -156,7 +162,7 @@ void start_frame(void);
|
||||
void end_frame(void);
|
||||
void set_screen(C3D_RenderTarget * screen);
|
||||
|
||||
void throw_error(char* error, ErrorLevel level);
|
||||
void throw_error(const char* error, ErrorLevel level);
|
||||
bool draw_confirm(const char* conf_msg, Entry_List_s* list);
|
||||
|
||||
void draw_preview(C2D_Image preview, int preview_offset);
|
||||
@@ -173,4 +179,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
|
||||
|
||||
15
include/fs.h
15
include/fs.h
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Anemone3DS
|
||||
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
|
||||
* Copyright (C) 2016-2020 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,11 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
FS_Archive ArchiveSD;
|
||||
FS_Archive ArchiveHomeExt;
|
||||
FS_Archive ArchiveThemeExt;
|
||||
#define ILLEGAL_CHARS "><\"?;:/\\+,.|[=]"
|
||||
|
||||
extern FS_Archive ArchiveSD;
|
||||
extern FS_Archive ArchiveHomeExt;
|
||||
extern FS_Archive ArchiveThemeExt;
|
||||
|
||||
Result open_archives(void);
|
||||
Result close_archives(void);
|
||||
@@ -39,8 +41,11 @@ 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Anemone3DS
|
||||
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
|
||||
* Copyright (C) 2016-2020 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",
|
||||
NULL
|
||||
"\uE005 Dumping menu"
|
||||
},
|
||||
{
|
||||
"Exit",
|
||||
@@ -140,6 +140,27 @@ 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Anemone3DS
|
||||
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
|
||||
* Copyright (C) 2016-2020 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Anemone3DS
|
||||
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
|
||||
* Copyright (C) 2016-2020 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 40960 // How much data should be buffered at a time
|
||||
#define BUF_TO_READ 48000 // 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Anemone3DS
|
||||
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
|
||||
* Copyright (C) 2016-2020 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,8 +29,9 @@
|
||||
|
||||
#include "common.h"
|
||||
#include "draw.h"
|
||||
#include <ctype.h>
|
||||
|
||||
#define THEMEPLAZA_BASE_URL "https://themeplaza.eu"
|
||||
#define THEMEPLAZA_BASE_URL "http://themeplaza.art"
|
||||
#define THEMEPLAZA_API_URL "/api/anemone/v1"
|
||||
#define THEMEPLAZA_BASE_API_URL THEMEPLAZA_BASE_URL THEMEPLAZA_API_URL
|
||||
|
||||
@@ -51,6 +52,6 @@
|
||||
#define CACHE_PATH_FORMAT "/3ds/" APP_TITLE "/cache/%" JSON_INTEGER_FORMAT
|
||||
|
||||
bool themeplaza_browser(EntryMode mode);
|
||||
u32 http_get(const char *url, char ** filename, char ** buf, InstallType install_type);
|
||||
Result http_get(const char *url, char ** filename, char ** buf, u32 * size, InstallType install_type, const char * acceptable_mime_types);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Anemone3DS
|
||||
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
|
||||
* Copyright (C) 2016-2020 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Anemone3DS
|
||||
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
|
||||
* Copyright (C) 2016-2020 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,8 +49,9 @@ typedef struct {
|
||||
u8 _padding1[0x13b8];
|
||||
ThemeEntry_s theme_entry;
|
||||
ThemeEntry_s shuffle_themes[MAX_SHUFFLE_THEMES];
|
||||
u8 _padding2[0xb];
|
||||
bool shuffle;
|
||||
u8 shuffle_seedA[0xb];
|
||||
u8 shuffle;
|
||||
u8 shuffle_seedB[0xa];
|
||||
} SaveData_dat_s;
|
||||
|
||||
typedef struct {
|
||||
@@ -75,6 +76,9 @@ 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Anemone3DS
|
||||
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
|
||||
* Copyright (C) 2016-2020 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
|
||||
|
||||
474
source/camera.c
474
source/camera.c
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Anemone3DS
|
||||
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
|
||||
* Copyright (C) 2016-2020 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,28 +36,63 @@
|
||||
#include <archive.h>
|
||||
#include <archive_entry.h>
|
||||
|
||||
void exit_qr(qr_data *data)
|
||||
static void start_read(qr_data *data)
|
||||
{
|
||||
DEBUG("Exiting QR\n");
|
||||
svcSignalEvent(data->cancel);
|
||||
while(!data->finished)
|
||||
svcSleepThread(1000000);
|
||||
svcCloseHandle(data->cancel);
|
||||
data->capturing = false;
|
||||
LightLock_Lock(&data->mut);
|
||||
while(data->writer_waiting || data->writer_active)
|
||||
{
|
||||
CondVar_WaitTimeout(&data->cond, &data->mut, 1000000);
|
||||
}
|
||||
|
||||
free(data->camera_buffer);
|
||||
free(data->tex);
|
||||
quirc_destroy(data->context);
|
||||
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);
|
||||
}
|
||||
|
||||
void capture_cam_thread(void *arg)
|
||||
static void capture_cam_thread(void *arg)
|
||||
{
|
||||
qr_data *data = (qr_data *) arg;
|
||||
Handle events[3] = {0};
|
||||
events[0] = data->cancel;
|
||||
u32 transferUnit;
|
||||
|
||||
u16 *buffer = calloc(1, 400 * 240 * sizeof(u16));
|
||||
Handle cam_events[3] = {0};
|
||||
cam_events[0] = data->event_stop;
|
||||
|
||||
u32 transferUnit;
|
||||
const u32 bufsz = 400 * 240 * sizeof(u16);
|
||||
u16 *buffer = linearAlloc(bufsz);
|
||||
|
||||
camInit();
|
||||
CAMU_SetSize(SELECT_OUT1, SIZE_CTR_TOP_LCD, CONTEXT_A);
|
||||
CAMU_SetOutputFormat(SELECT_OUT1, OUTPUT_RGB_565, CONTEXT_A);
|
||||
@@ -66,38 +101,42 @@ void capture_cam_thread(void *arg)
|
||||
CAMU_SetAutoExposure(SELECT_OUT1, true);
|
||||
CAMU_SetAutoWhiteBalance(SELECT_OUT1, true);
|
||||
CAMU_Activate(SELECT_OUT1);
|
||||
CAMU_GetBufferErrorInterruptEvent(&events[2], PORT_CAM1);
|
||||
CAMU_GetBufferErrorInterruptEvent(&cam_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(&events[1], buffer, PORT_CAM1, 400 * 240 * sizeof(u16), (s16) transferUnit);
|
||||
CAMU_SetReceiving(&cam_events[1], buffer, PORT_CAM1, bufsz, transferUnit);
|
||||
CAMU_StartCapture(PORT_CAM1);
|
||||
svcSignalEvent(data->started);
|
||||
|
||||
bool cancel = false;
|
||||
while (!cancel)
|
||||
|
||||
while (!cancel)
|
||||
{
|
||||
s32 index = 0;
|
||||
svcWaitSynchronizationN(&index, events, 3, false, U64_MAX);
|
||||
svcWaitSynchronizationN(&index, cam_events, 3, false, U64_MAX);
|
||||
switch(index) {
|
||||
case 0:
|
||||
DEBUG("Cancel event received\n");
|
||||
cancel = true;
|
||||
break;
|
||||
case 1:
|
||||
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);
|
||||
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);
|
||||
break;
|
||||
case 2:
|
||||
svcCloseHandle(events[1]);
|
||||
events[1] = 0;
|
||||
svcCloseHandle(cam_events[1]);
|
||||
cam_events[1] = 0;
|
||||
|
||||
CAMU_ClearBuffer(PORT_CAM1);
|
||||
CAMU_SetReceiving(&events[1], buffer, PORT_CAM1, 400 * 240 * sizeof(u16), transferUnit);
|
||||
CAMU_SetReceiving(&cam_events[1], buffer, PORT_CAM1, bufsz, transferUnit);
|
||||
CAMU_StartCapture(PORT_CAM1);
|
||||
break;
|
||||
default:
|
||||
@@ -115,213 +154,266 @@ void capture_cam_thread(void *arg)
|
||||
CAMU_ClearBuffer(PORT_CAM1);
|
||||
CAMU_Activate(SELECT_NONE);
|
||||
camExit();
|
||||
free(buffer);
|
||||
for(int i = 0; i < 3; i++) {
|
||||
if(events[i] != 0) {
|
||||
svcCloseHandle(events[i]);
|
||||
events[i] = 0;
|
||||
|
||||
linearFree(buffer);
|
||||
for(int i = 1; i < 3; i++)
|
||||
{
|
||||
if(cam_events[i] != 0) {
|
||||
svcCloseHandle(cam_events[i]);
|
||||
cam_events[i] = 0;
|
||||
}
|
||||
}
|
||||
svcCloseHandle(data->mutex);
|
||||
data->finished = true;
|
||||
|
||||
LightEvent_Signal(&data->event_cam_info);
|
||||
}
|
||||
|
||||
void update_ui(void *arg)
|
||||
static void update_ui(void *arg)
|
||||
{
|
||||
qr_data* data = (qr_data*) arg;
|
||||
C3D_Tex tex;
|
||||
|
||||
while (!data->finished)
|
||||
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
|
||||
{
|
||||
draw_base_interface();
|
||||
|
||||
// Untiled texture loading code adapted from FBI
|
||||
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);
|
||||
|
||||
memcpy(&((u8*) data->image.tex->data)[dstPos], &((u8*) data->camera_buffer)[srcPos], sizeof(u16));
|
||||
}
|
||||
}
|
||||
|
||||
svcReleaseMutex(data->mutex);
|
||||
|
||||
if (data->finished)
|
||||
start_read(data);
|
||||
if(data->any_update)
|
||||
{
|
||||
end_frame();
|
||||
}
|
||||
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)));
|
||||
|
||||
C2D_DrawImageAt(data->image, 0.0f, 0.0f, 0.4f, NULL, 1.0f, 1.0f);
|
||||
((u16*)tex.data)[dstPos] = data->camera_buffer[srcPos + x];
|
||||
}
|
||||
}
|
||||
data->any_update = false;
|
||||
}
|
||||
stop_read(data);
|
||||
|
||||
C2D_DrawImageAt((C2D_Image){ &tex, &subt3x }, 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);
|
||||
}
|
||||
|
||||
bool start_capture_cam(qr_data *data)
|
||||
static bool start_capture_cam(qr_data *data)
|
||||
{
|
||||
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;
|
||||
svcWaitSynchronization(data->started, U64_MAX);
|
||||
if(threadCreate(update_ui, data, 0x10000, 0x1A, 1, true) == NULL)
|
||||
if((data->cam_thread = threadCreate(capture_cam_thread, data, 0x10000, 0x1A, 1, false)) == NULL)
|
||||
{
|
||||
exit_qr(data);
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
if((data->ui_thread = threadCreate(update_ui, data, 0x10000, 0x1A, 1, false)) == NULL)
|
||||
{
|
||||
LightEvent_Signal(&data->event_ui_info);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void update_qr(qr_data *data)
|
||||
static bool update_qr(qr_data *data, struct quirc_data* scan_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);
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
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))
|
||||
{
|
||||
exit_qr(data);
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
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("File downloaded isn't a zip.", ERROR_LEVEL_WARNING);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw_error("Download failed.", ERROR_LEVEL_WARNING);
|
||||
}
|
||||
|
||||
free(filename);
|
||||
free(zip_buf);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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 = 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);
|
||||
qr_data data;
|
||||
|
||||
data->camera_buffer = calloc(1, 400 * 240 * sizeof(u16));
|
||||
memset(&data, 0, sizeof(data));
|
||||
|
||||
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);
|
||||
start_qr(&data);
|
||||
|
||||
while (!data->finished) update_qr(data);
|
||||
bool success = data->success;
|
||||
free(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;
|
||||
|
||||
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)
|
||||
{
|
||||
save_zip_to_sd(filename, zip_size, zip_buf, mode);
|
||||
success = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw_error("Zip downloaded is neither\na splash nor a theme.", ERROR_LEVEL_WARNING);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw_error("File downloaded isn't a zip.", ERROR_LEVEL_WARNING);
|
||||
}
|
||||
free(zip_buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw_error("Download failed.", ERROR_LEVEL_WARNING);
|
||||
}
|
||||
|
||||
free(filename);
|
||||
}
|
||||
|
||||
free(scan_data);
|
||||
quirc_destroy(data.context);
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Anemone3DS
|
||||
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
|
||||
* Copyright (C) 2016-2020 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Anemone3DS
|
||||
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
|
||||
* Copyright (C) 2016-2020 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,6 +125,8 @@ 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]);
|
||||
@@ -274,7 +276,7 @@ void draw_base_interface(void)
|
||||
set_screen(top);
|
||||
}
|
||||
|
||||
void throw_error(char* error, ErrorLevel level)
|
||||
void throw_error(const char* error, ErrorLevel level)
|
||||
{
|
||||
Text bottom_text = TEXT_AMOUNT;
|
||||
Color text_color = COLOR_WHITE;
|
||||
@@ -300,7 +302,7 @@ void throw_error(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, 150, 0.5f, 0.6f, 0.6f, colors[COLOR_WHITE], &text[bottom_text]);
|
||||
draw_c2d_text_center(GFX_TOP, 170, 0.5f, 0.6f, 0.6f, colors[COLOR_WHITE], &text[bottom_text]);
|
||||
end_frame();
|
||||
|
||||
if(kDown & KEY_A) break;
|
||||
@@ -434,7 +436,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(fontGlyphIndexFromCodePoint(codepoint))->charWidth);
|
||||
float character_width = scaleX * (fontGetCharWidthInfo(NULL, fontGlyphIndexFromCodePoint(NULL, codepoint))->charWidth);
|
||||
if((current_width += character_width) > max_width)
|
||||
{
|
||||
char* last_space = NULL;
|
||||
@@ -552,13 +554,18 @@ 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)
|
||||
{
|
||||
|
||||
294
source/fs.c
294
source/fs.c
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Anemone3DS
|
||||
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
|
||||
* Copyright (C) 2016-2020 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,25 +25,30 @@
|
||||
*/
|
||||
|
||||
#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(®ionCode);
|
||||
switch(regionCode)
|
||||
{
|
||||
@@ -63,7 +68,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);
|
||||
@@ -71,43 +76,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)
|
||||
@@ -172,6 +177,7 @@ 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");
|
||||
@@ -190,7 +196,149 @@ 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;
|
||||
@@ -203,4 +351,116 @@ 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);
|
||||
}
|
||||
|
||||
123
source/loading.c
123
source/loading.c
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Anemone3DS
|
||||
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
|
||||
* Copyright (C) 2016-2020 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,6 +83,7 @@ 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);
|
||||
@@ -92,27 +93,12 @@ 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;
|
||||
@@ -120,7 +106,9 @@ static C2D_Image * load_entry_icon(Entry_s entry)
|
||||
if(!size) return NULL;
|
||||
|
||||
Icon_s * smdh = (Icon_s *)info_buffer;
|
||||
return loadTextureIcon(smdh);
|
||||
C2D_Image* out = loadTextureIcon(smdh);
|
||||
free(info_buffer);
|
||||
return out;
|
||||
}
|
||||
|
||||
typedef int (*sort_comparator)(const void *, const void *);
|
||||
@@ -187,17 +175,34 @@ 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)
|
||||
{
|
||||
free(list->entries);
|
||||
list->entries = NULL;
|
||||
res = -1;
|
||||
DEBUG("break\n");
|
||||
// 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);
|
||||
break;
|
||||
}
|
||||
else
|
||||
@@ -205,12 +210,11 @@ 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);
|
||||
|
||||
struacat(current_entry->path, loading_path);
|
||||
strucat(current_entry->path, dir_entry.name);
|
||||
|
||||
memcpy(current_entry->path, path, 0x106 * sizeof(u16));
|
||||
current_entry->is_zip = !strcmp(dir_entry.shortExt, "ZIP");
|
||||
parse_entry_smdh(current_entry, dir_entry.name);
|
||||
}
|
||||
|
||||
FSDIR_Close(dir_handle);
|
||||
@@ -345,19 +349,18 @@ 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 + absdelta;
|
||||
int endi = starti + abs(delta);
|
||||
|
||||
if(delta < 0)
|
||||
{
|
||||
endi -= absdelta + 1;
|
||||
starti += absdelta - 1;
|
||||
endi -= abs(delta) + 1;
|
||||
starti += abs(delta) - 1;
|
||||
}
|
||||
|
||||
int ctr = 0;
|
||||
Entry_s ** entries = calloc(absdelta, sizeof(Entry_s *));
|
||||
int * indexes = calloc(absdelta, sizeof(int));
|
||||
Entry_s ** entries = calloc(abs(delta), sizeof(Entry_s *));
|
||||
int * indexes = calloc(abs(delta), sizeof(int));
|
||||
bool released = false;
|
||||
|
||||
C2D_Image ** icons = current_list->icons;
|
||||
@@ -390,54 +393,36 @@ static bool load_icons(Entry_List_s * current_list, Handle mutex)
|
||||
indexes[ctr] = index;
|
||||
}
|
||||
|
||||
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)
|
||||
#undef SIGN
|
||||
|
||||
if(abs(delta) < 4)
|
||||
{
|
||||
svcReleaseMutex(mutex);
|
||||
released = true;
|
||||
svcSleepThread(1e7);
|
||||
}
|
||||
|
||||
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)
|
||||
svcSleepThread(1e7);
|
||||
starti = 0;
|
||||
endi = abs(delta);
|
||||
for(int i = starti; i < endi; i++)
|
||||
{
|
||||
Entry_s * current_entry = entries[i];
|
||||
int index = indexes[i];
|
||||
|
||||
C2D_Image * image = icons[index];
|
||||
C3D_TexDelete(image->tex);
|
||||
free(image->tex);
|
||||
free(image);
|
||||
if (icons[index] != NULL)
|
||||
{
|
||||
C3D_TexDelete(image->tex);
|
||||
free(image->tex);
|
||||
free(image);
|
||||
}
|
||||
|
||||
icons[index] = load_entry_icon(*current_entry);
|
||||
|
||||
if(!released && i == actual_cutoff)
|
||||
if(!released && i > endi/2)
|
||||
{
|
||||
svcReleaseMutex(mutex);
|
||||
released = true;
|
||||
svcSleepThread(1e7);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -513,7 +498,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_filler(png, 0xFF, PNG_FILLER_AFTER);
|
||||
png_set_add_alpha(png, 0xFF, PNG_FILLER_BEFORE);
|
||||
|
||||
if(color_type == PNG_COLOR_TYPE_GRAY ||
|
||||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||||
@@ -562,8 +547,12 @@ 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;
|
||||
@@ -663,4 +652,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Anemone3DS
|
||||
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
|
||||
* Copyright (C) 2016-2020 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,6 +132,7 @@ 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]);
|
||||
@@ -239,6 +240,7 @@ 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)
|
||||
{
|
||||
@@ -316,7 +318,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++;
|
||||
@@ -358,7 +360,6 @@ 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};
|
||||
@@ -400,14 +401,13 @@ 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(qr_mode) take_picture();
|
||||
else if(preview_mode)
|
||||
if(preview_mode)
|
||||
{
|
||||
draw_preview(preview, preview_offset);
|
||||
}
|
||||
@@ -439,14 +439,14 @@ int main(void)
|
||||
|
||||
if(!install_mode && !extra_mode)
|
||||
{
|
||||
if(!preview_mode && !qr_mode && kDown & KEY_L) //toggle between splashes and themes
|
||||
if(!preview_mode && kDown & KEY_L) //toggle between splashes and themes
|
||||
{
|
||||
switch_mode:
|
||||
current_mode++;
|
||||
current_mode %= MODE_AMOUNT;
|
||||
continue;
|
||||
}
|
||||
else if(!qr_mode && !preview_mode && kDown & KEY_R) //toggle QR mode
|
||||
else if(!preview_mode && kDown & KEY_R) //toggle QR mode
|
||||
{
|
||||
enable_qr:
|
||||
draw_base_interface();
|
||||
@@ -479,7 +479,7 @@ int main(void)
|
||||
|
||||
continue;
|
||||
}
|
||||
else if(!qr_mode && kDown & KEY_Y && current_list->entries != NULL) //toggle preview mode
|
||||
else if(kDown & KEY_Y && current_list->entries != NULL) //toggle preview mode
|
||||
{
|
||||
toggle_preview:
|
||||
if(!preview_mode)
|
||||
@@ -524,7 +524,7 @@ int main(void)
|
||||
}
|
||||
}
|
||||
|
||||
if(qr_mode || preview_mode || current_list->entries == NULL)
|
||||
if(preview_mode || current_list->entries == NULL)
|
||||
goto touch;
|
||||
|
||||
int selected_entry = current_list->selected_entry;
|
||||
@@ -670,6 +670,23 @@ 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;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Anemone3DS
|
||||
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
|
||||
* Copyright (C) 2016-2020 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
|
||||
|
||||
@@ -576,10 +576,11 @@ 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;
|
||||
int bc = ver->data_bytes / sb_ecc->bs;
|
||||
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 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));
|
||||
@@ -588,22 +589,16 @@ 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 = sb_ecc;
|
||||
const struct quirc_rs_params *ecc =
|
||||
(i < sb_ecc->ns) ? sb_ecc : &lb_ecc;
|
||||
const int num_ec = ecc->bs - ecc->dw;
|
||||
quirc_decode_error_t err;
|
||||
int j = 0;
|
||||
int k;
|
||||
int j;
|
||||
|
||||
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];
|
||||
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];
|
||||
|
||||
err = correct_block(dst, ecc);
|
||||
if (err)
|
||||
@@ -729,10 +724,10 @@ static quirc_decode_error_t decode_alpha(struct quirc_data *data,
|
||||
int bits = 13;
|
||||
int count;
|
||||
|
||||
if (data->version < 7)
|
||||
if (data->version < 10)
|
||||
bits = 9;
|
||||
else if (data->version < 11)
|
||||
bits = 10;
|
||||
else if (data->version < 27)
|
||||
bits = 11;
|
||||
|
||||
count = take_bits(ds, bits);
|
||||
if (data->payload_len + count + 1 > QUIRC_MAX_PAYLOAD)
|
||||
@@ -795,12 +790,18 @@ 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 (d + 0x8140 >= 0x9ffc)
|
||||
sjw = d + 0x8140;
|
||||
else
|
||||
sjw = d + 0xc140;
|
||||
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;
|
||||
}
|
||||
|
||||
data->payload[data->payload_len++] = sjw >> 8;
|
||||
data->payload[data->payload_len++] = sjw & 0xff;
|
||||
@@ -873,7 +874,7 @@ static quirc_decode_error_t decode_payload(struct quirc_data *data,
|
||||
done:
|
||||
|
||||
/* Add nul terminator to all payloads */
|
||||
if ((unsigned int)data->payload_len >= sizeof(data->payload))
|
||||
if (data->payload_len >= (int) sizeof(data->payload))
|
||||
data->payload_len--;
|
||||
data->payload[data->payload_len] = 0;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
@@ -98,8 +99,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 = rint(x);
|
||||
ret->y = rint(y);
|
||||
ret->x = (int) rint(x);
|
||||
ret->y = (int) rint(y);
|
||||
}
|
||||
|
||||
static void perspective_unmap(const double *c,
|
||||
@@ -121,234 +122,113 @@ 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);
|
||||
|
||||
#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)
|
||||
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)
|
||||
{
|
||||
int left = x;
|
||||
int right = x;
|
||||
int i;
|
||||
quirc_pixel_t *row = ffp.q->pixels + y * ffp.q->w;
|
||||
quirc_pixel_t *row = q->pixels + y * q->w;
|
||||
|
||||
if (!depth)
|
||||
if (depth >= FLOOD_FILL_MAX_DEPTH)
|
||||
return;
|
||||
|
||||
while (left > 0 && row[left - 1] == ffp.from)
|
||||
while (left > 0 && row[left - 1] == from)
|
||||
left--;
|
||||
|
||||
while (right < ffp.q->w - 1 && row[right + 1] == ffp.from)
|
||||
while (right < q->w - 1 && row[right + 1] == from)
|
||||
right++;
|
||||
|
||||
/* Fill the extent */
|
||||
for (i = left; i <= right; i++)
|
||||
row[i] = ffp.to;
|
||||
row[i] = to;
|
||||
|
||||
if (ffp.func)
|
||||
ffp.func(ffp.user_data, y, left, right);
|
||||
if (func)
|
||||
func(user_data, y, left, right);
|
||||
|
||||
/* Seed new flood-fills */
|
||||
if (y > 0) {
|
||||
row = ffp.q->pixels + (y - 1) * ffp.q->w;
|
||||
row = q->pixels + (y - 1) * q->w;
|
||||
|
||||
for (i = left; i <= right; i++)
|
||||
if (row[i] == ffp.from)
|
||||
flood_fill_rec(i, y - 1, depth - 1);
|
||||
if (row[i] == from)
|
||||
flood_fill_seed(q, i, y - 1, from, to,
|
||||
func, user_data, depth + 1);
|
||||
}
|
||||
|
||||
if (y < ffp.q->h - 1) {
|
||||
row = ffp.q->pixels + (y + 1) * ffp.q->w;
|
||||
if (y < q->h - 1) {
|
||||
row = q->pixels + (y + 1) * q->w;
|
||||
|
||||
for (i = left; i <= right; i++)
|
||||
if (row[i] == ffp.from)
|
||||
flood_fill_rec(i, y + 1, depth - 1);
|
||||
if (row[i] == from)
|
||||
flood_fill_seed(q, i, y + 1, from, to,
|
||||
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
|
||||
*/
|
||||
|
||||
#define THRESHOLD_S_DEN 8
|
||||
#define THRESHOLD_T 5
|
||||
|
||||
static void threshold(struct quirc *q)
|
||||
static uint8_t otsu(const struct quirc *q)
|
||||
{
|
||||
int x, y;
|
||||
int avg_w = 0;
|
||||
int avg_u = 0;
|
||||
int threshold_s = q->w / THRESHOLD_S_DEN;
|
||||
quirc_pixel_t *row = q->pixels;
|
||||
int numPixels = q->w * q->h;
|
||||
|
||||
for (y = 0; y < q->h; y++) {
|
||||
int row_average[q->w];
|
||||
|
||||
memset(row_average, 0, sizeof(row_average));
|
||||
|
||||
for (x = 0; x < q->w; x++) {
|
||||
int w, u;
|
||||
|
||||
if (y & 1) {
|
||||
w = x;
|
||||
u = q->w - 1 - x;
|
||||
} else {
|
||||
w = q->w - 1 - x;
|
||||
u = x;
|
||||
}
|
||||
|
||||
avg_w = (avg_w * (threshold_s - 1)) /
|
||||
threshold_s + row[w];
|
||||
avg_u = (avg_u * (threshold_s - 1)) /
|
||||
threshold_s + row[u];
|
||||
|
||||
row_average[w] += avg_w;
|
||||
row_average[u] += avg_u;
|
||||
}
|
||||
|
||||
for (x = 0; x < q->w; x++) {
|
||||
if (row[x] < row_average[x] *
|
||||
(100 - THRESHOLD_T) / (200 * threshold_s))
|
||||
row[x] = QUIRC_PIXEL_BLACK;
|
||||
else
|
||||
row[x] = QUIRC_PIXEL_WHITE;
|
||||
}
|
||||
|
||||
row += q->w;
|
||||
// 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]++;
|
||||
}
|
||||
|
||||
// Calculate weighted sum of histogram values
|
||||
int sum = 0;
|
||||
for (int i = 0; i < HISTOGRAM_SIZE; ++i) {
|
||||
sum += i * histogram[i];
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// Weighted foreground
|
||||
const int q2 = numPixels - q1;
|
||||
if (q2 == 0)
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return threshold;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -381,7 +261,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);
|
||||
flood_fill_seed(q, x, y, pixel, region, area_count, box, 0);
|
||||
|
||||
return region;
|
||||
}
|
||||
@@ -451,7 +331,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);
|
||||
find_one_corner, &psd, 0);
|
||||
|
||||
psd.ref.x = psd.corners[0].x - psd.ref.x;
|
||||
psd.ref.y = psd.corners[0].y - psd.ref.y;
|
||||
@@ -469,7 +349,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);
|
||||
find_other_corners, &psd, 0);
|
||||
}
|
||||
|
||||
static void record_capstone(struct quirc *q, int ring, int stone)
|
||||
@@ -542,7 +422,7 @@ static void finder_scan(struct quirc *q, int y)
|
||||
{
|
||||
quirc_pixel_t *row = q->pixels + y * q->w;
|
||||
int x;
|
||||
int last_color;
|
||||
int last_color = 0;
|
||||
int run_length = 0;
|
||||
int run_count = 0;
|
||||
int pb[5];
|
||||
@@ -888,7 +768,7 @@ static int fitness_all(const struct quirc *q, int index)
|
||||
|
||||
/* Check alignment patterns */
|
||||
ap_count = 0;
|
||||
while (info->apat[ap_count])
|
||||
while ((ap_count < QUIRC_MAX_ALIGNMENT) && info->apat[ap_count])
|
||||
ap_count++;
|
||||
|
||||
for (i = 1; i + 1 < ap_count; i++) {
|
||||
@@ -974,7 +854,7 @@ static void rotate_capstone(struct quirc_capstone *cap,
|
||||
struct quirc_point copy[4];
|
||||
int j;
|
||||
int best = 0;
|
||||
int best_score = 0;
|
||||
int best_score = INT_MAX;
|
||||
|
||||
for (j = 0; j < 4; j++) {
|
||||
struct quirc_point *p = &cap->corners[j];
|
||||
@@ -1082,10 +962,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);
|
||||
NULL, NULL, 0);
|
||||
flood_fill_seed(q, reg->seed.x, reg->seed.y,
|
||||
QUIRC_PIXEL_BLACK, qr->align_region,
|
||||
find_leftmost_to_line, &psd);
|
||||
find_leftmost_to_line, &psd, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1191,17 +1071,18 @@ static void test_grouping(struct quirc *q, int i)
|
||||
test_neighbours(q, i, &hlist, &vlist);
|
||||
}
|
||||
|
||||
static void pixels_setup(struct quirc *q)
|
||||
static void pixels_setup(struct quirc *q, uint8_t threshold)
|
||||
{
|
||||
if (sizeof(*q->image) == sizeof(*q->pixels)) {
|
||||
if (QUIRC_PIXEL_ALIAS_IMAGE) {
|
||||
q->pixels = (quirc_pixel_t *)q->image;
|
||||
} else {
|
||||
int x, y;
|
||||
for (y = 0; y < q->h; y++) {
|
||||
for (x = 0; x < q->w; x++) {
|
||||
q->pixels[y * q->w + x] = q->image[y * q->w + x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t* 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1223,8 +1104,8 @@ void quirc_end(struct quirc *q)
|
||||
{
|
||||
int i;
|
||||
|
||||
pixels_setup(q);
|
||||
threshold(q);
|
||||
uint8_t threshold = otsu(q);
|
||||
pixels_setup(q, threshold);
|
||||
|
||||
for (i = 0; i < q->h; i++)
|
||||
finder_scan(q, i);
|
||||
|
||||
@@ -36,34 +36,73 @@ struct quirc *quirc_new(void)
|
||||
|
||||
void quirc_destroy(struct quirc *q)
|
||||
{
|
||||
if (q->image)
|
||||
free(q->image);
|
||||
if (sizeof(*q->image) != sizeof(*q->pixels))
|
||||
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)
|
||||
free(q->pixels);
|
||||
|
||||
free(q);
|
||||
}
|
||||
|
||||
int quirc_resize(struct quirc *q, int w, int h)
|
||||
{
|
||||
uint8_t *new_image = realloc(q->image, w * h);
|
||||
uint8_t *image = NULL;
|
||||
quirc_pixel_t *pixels = NULL;
|
||||
|
||||
if (!new_image)
|
||||
return -1;
|
||||
/*
|
||||
* 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 (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 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;
|
||||
}
|
||||
|
||||
q->image = new_image;
|
||||
/* alloc succeeded, update `q` with the new size and buffers */
|
||||
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)
|
||||
@@ -84,7 +123,6 @@ 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];
|
||||
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct quirc;
|
||||
|
||||
/* Obtain the library version string. */
|
||||
@@ -117,7 +121,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.
|
||||
*/
|
||||
@@ -162,4 +166,8 @@ 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
|
||||
|
||||
@@ -32,8 +32,10 @@
|
||||
#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"
|
||||
@@ -98,9 +100,9 @@ struct quirc {
|
||||
#define QUIRC_MAX_ALIGNMENT 7
|
||||
|
||||
struct quirc_rs_params {
|
||||
int bs; /* Block size */
|
||||
int dw; /* Data words */
|
||||
int ce; /* Correctable errors */
|
||||
int bs; /* Small block size */
|
||||
int dw; /* Small data words */
|
||||
int ns; /* Number of small blocks */
|
||||
};
|
||||
|
||||
struct quirc_version_info {
|
||||
|
||||
@@ -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, .ce = 4},
|
||||
{.bs = 26, .dw = 19, .ce = 2},
|
||||
{.bs = 26, .dw = 9, .ce = 8},
|
||||
{.bs = 26, .dw = 13, .ce = 6}
|
||||
{.bs = 26, .dw = 16, .ns = 1},
|
||||
{.bs = 26, .dw = 19, .ns = 1},
|
||||
{.bs = 26, .dw = 9, .ns = 1},
|
||||
{.bs = 26, .dw = 13, .ns = 1}
|
||||
}
|
||||
},
|
||||
{ /* Version 2 */
|
||||
.data_bytes = 44,
|
||||
.apat = {6, 18, 0},
|
||||
.ecc = {
|
||||
{.bs = 44, .dw = 28, .ce = 8},
|
||||
{.bs = 44, .dw = 34, .ce = 4},
|
||||
{.bs = 44, .dw = 16, .ce = 14},
|
||||
{.bs = 44, .dw = 22, .ce = 11}
|
||||
{.bs = 44, .dw = 28, .ns = 1},
|
||||
{.bs = 44, .dw = 34, .ns = 1},
|
||||
{.bs = 44, .dw = 16, .ns = 1},
|
||||
{.bs = 44, .dw = 22, .ns = 1}
|
||||
}
|
||||
},
|
||||
{ /* Version 3 */
|
||||
.data_bytes = 70,
|
||||
.apat = {6, 22, 0},
|
||||
.ecc = {
|
||||
{.bs = 70, .dw = 44, .ce = 13},
|
||||
{.bs = 70, .dw = 55, .ce = 7},
|
||||
{.bs = 35, .dw = 13, .ce = 11},
|
||||
{.bs = 35, .dw = 17, .ce = 9}
|
||||
{.bs = 70, .dw = 44, .ns = 1},
|
||||
{.bs = 70, .dw = 55, .ns = 1},
|
||||
{.bs = 35, .dw = 13, .ns = 2},
|
||||
{.bs = 35, .dw = 17, .ns = 2}
|
||||
}
|
||||
},
|
||||
{ /* Version 4 */
|
||||
.data_bytes = 100,
|
||||
.apat = {6, 26, 0},
|
||||
.ecc = {
|
||||
{.bs = 50, .dw = 32, .ce = 9},
|
||||
{.bs = 100, .dw = 80, .ce = 10},
|
||||
{.bs = 25, .dw = 9, .ce = 8},
|
||||
{.bs = 50, .dw = 24, .ce = 13}
|
||||
{.bs = 50, .dw = 32, .ns = 2},
|
||||
{.bs = 100, .dw = 80, .ns = 1},
|
||||
{.bs = 25, .dw = 9, .ns = 4},
|
||||
{.bs = 50, .dw = 24, .ns = 2}
|
||||
}
|
||||
},
|
||||
{ /* Version 5 */
|
||||
.data_bytes = 134,
|
||||
.apat = {6, 30, 0},
|
||||
.ecc = {
|
||||
{.bs = 67, .dw = 43, .ce = 12},
|
||||
{.bs = 134, .dw = 108, .ce = 13},
|
||||
{.bs = 33, .dw = 11, .ce = 11},
|
||||
{.bs = 33, .dw = 15, .ce = 9}
|
||||
{.bs = 67, .dw = 43, .ns = 2},
|
||||
{.bs = 134, .dw = 108, .ns = 1},
|
||||
{.bs = 33, .dw = 11, .ns = 2},
|
||||
{.bs = 33, .dw = 15, .ns = 2}
|
||||
}
|
||||
},
|
||||
{ /* Version 6 */
|
||||
.data_bytes = 172,
|
||||
.apat = {6, 34, 0},
|
||||
.ecc = {
|
||||
{.bs = 43, .dw = 27, .ce = 8},
|
||||
{.bs = 86, .dw = 68, .ce = 9},
|
||||
{.bs = 43, .dw = 15, .ce = 14},
|
||||
{.bs = 43, .dw = 19, .ce = 12}
|
||||
{.bs = 43, .dw = 27, .ns = 4},
|
||||
{.bs = 86, .dw = 68, .ns = 2},
|
||||
{.bs = 43, .dw = 15, .ns = 4},
|
||||
{.bs = 43, .dw = 19, .ns = 4}
|
||||
}
|
||||
},
|
||||
{ /* Version 7 */
|
||||
.data_bytes = 196,
|
||||
.apat = {6, 22, 38, 0},
|
||||
.ecc = {
|
||||
{.bs = 49, .dw = 31, .ce = 9},
|
||||
{.bs = 98, .dw = 78, .ce = 10},
|
||||
{.bs = 39, .dw = 13, .ce = 13},
|
||||
{.bs = 32, .dw = 14, .ce = 9}
|
||||
{.bs = 49, .dw = 31, .ns = 4},
|
||||
{.bs = 98, .dw = 78, .ns = 2},
|
||||
{.bs = 39, .dw = 13, .ns = 4},
|
||||
{.bs = 32, .dw = 14, .ns = 2}
|
||||
}
|
||||
},
|
||||
{ /* Version 8 */
|
||||
.data_bytes = 242,
|
||||
.apat = {6, 24, 42, 0},
|
||||
.ecc = {
|
||||
{.bs = 60, .dw = 38, .ce = 11},
|
||||
{.bs = 121, .dw = 97, .ce = 12},
|
||||
{.bs = 40, .dw = 14, .ce = 13},
|
||||
{.bs = 40, .dw = 18, .ce = 11}
|
||||
{.bs = 60, .dw = 38, .ns = 2},
|
||||
{.bs = 121, .dw = 97, .ns = 2},
|
||||
{.bs = 40, .dw = 14, .ns = 4},
|
||||
{.bs = 40, .dw = 18, .ns = 4}
|
||||
}
|
||||
},
|
||||
{ /* Version 9 */
|
||||
.data_bytes = 292,
|
||||
.apat = {6, 26, 46, 0},
|
||||
.ecc = {
|
||||
{.bs = 58, .dw = 36, .ce = 11},
|
||||
{.bs = 146, .dw = 116, .ce = 15},
|
||||
{.bs = 36, .dw = 12, .ce = 12},
|
||||
{.bs = 36, .dw = 16, .ce = 10}
|
||||
{.bs = 58, .dw = 36, .ns = 3},
|
||||
{.bs = 146, .dw = 116, .ns = 2},
|
||||
{.bs = 36, .dw = 12, .ns = 4},
|
||||
{.bs = 36, .dw = 16, .ns = 4}
|
||||
}
|
||||
},
|
||||
{ /* Version 10 */
|
||||
.data_bytes = 346,
|
||||
.apat = {6, 28, 50, 0},
|
||||
.ecc = {
|
||||
{.bs = 69, .dw = 43, .ce = 13},
|
||||
{.bs = 86, .dw = 68, .ce = 9},
|
||||
{.bs = 43, .dw = 15, .ce = 14},
|
||||
{.bs = 43, .dw = 19, .ce = 12}
|
||||
{.bs = 69, .dw = 43, .ns = 4},
|
||||
{.bs = 86, .dw = 68, .ns = 2},
|
||||
{.bs = 43, .dw = 15, .ns = 6},
|
||||
{.bs = 43, .dw = 19, .ns = 6}
|
||||
}
|
||||
},
|
||||
{ /* Version 11 */
|
||||
.data_bytes = 404,
|
||||
.apat = {6, 30, 54, 0},
|
||||
.ecc = {
|
||||
{.bs = 80, .dw = 50, .ce = 15},
|
||||
{.bs = 101, .dw = 81, .ce = 10},
|
||||
{.bs = 36, .dw = 12, .ce = 12},
|
||||
{.bs = 50, .dw = 22, .ce = 14}
|
||||
{.bs = 80, .dw = 50, .ns = 1},
|
||||
{.bs = 101, .dw = 81, .ns = 4},
|
||||
{.bs = 36, .dw = 12, .ns = 3},
|
||||
{.bs = 50, .dw = 22, .ns = 4}
|
||||
}
|
||||
},
|
||||
{ /* Version 12 */
|
||||
.data_bytes = 466,
|
||||
.apat = {6, 32, 58, 0},
|
||||
.ecc = {
|
||||
{.bs = 58, .dw = 36, .ce = 11},
|
||||
{.bs = 116, .dw = 92, .ce = 12},
|
||||
{.bs = 42, .dw = 14, .ce = 14},
|
||||
{.bs = 46, .dw = 20, .ce = 14}
|
||||
{.bs = 58, .dw = 36, .ns = 6},
|
||||
{.bs = 116, .dw = 92, .ns = 2},
|
||||
{.bs = 42, .dw = 14, .ns = 7},
|
||||
{.bs = 46, .dw = 20, .ns = 4}
|
||||
}
|
||||
},
|
||||
{ /* Version 13 */
|
||||
.data_bytes = 532,
|
||||
.apat = {6, 34, 62, 0},
|
||||
.ecc = {
|
||||
{.bs = 59, .dw = 37, .ce = 11},
|
||||
{.bs = 133, .dw = 107, .ce = 13},
|
||||
{.bs = 33, .dw = 11, .ce = 11},
|
||||
{.bs = 44, .dw = 20, .ce = 12}
|
||||
{.bs = 59, .dw = 37, .ns = 8},
|
||||
{.bs = 133, .dw = 107, .ns = 4},
|
||||
{.bs = 33, .dw = 11, .ns = 12},
|
||||
{.bs = 44, .dw = 20, .ns = 8}
|
||||
}
|
||||
},
|
||||
{ /* Version 14 */
|
||||
.data_bytes = 581,
|
||||
.apat = {6, 26, 46, 66, 0},
|
||||
.ecc = {
|
||||
{.bs = 65, .dw = 41, .ce = 12},
|
||||
{.bs = 109, .dw = 87, .ce = 11},
|
||||
{.bs = 36, .dw = 12, .ce = 12},
|
||||
{.bs = 54, .dw = 24, .ce = 15}
|
||||
{.bs = 64, .dw = 40, .ns = 4},
|
||||
{.bs = 145, .dw = 115, .ns = 3},
|
||||
{.bs = 36, .dw = 12, .ns = 11},
|
||||
{.bs = 36, .dw = 16, .ns = 11}
|
||||
}
|
||||
},
|
||||
{ /* Version 15 */
|
||||
.data_bytes = 655,
|
||||
.apat = {6, 26, 48, 70, 0},
|
||||
.ecc = {
|
||||
{.bs = 65, .dw = 41, .ce = 12},
|
||||
{.bs = 109, .dw = 87, .ce = 11},
|
||||
{.bs = 36, .dw = 12, .ce = 12},
|
||||
{.bs = 54, .dw = 24, .ce = 15}
|
||||
{.bs = 65, .dw = 41, .ns = 5},
|
||||
{.bs = 109, .dw = 87, .ns = 5},
|
||||
{.bs = 36, .dw = 12, .ns = 11},
|
||||
{.bs = 54, .dw = 24, .ns = 5}
|
||||
}
|
||||
},
|
||||
{ /* Version 16 */
|
||||
.data_bytes = 733,
|
||||
.apat = {6, 26, 50, 74, 0},
|
||||
.ecc = {
|
||||
{.bs = 73, .dw = 45, .ce = 14},
|
||||
{.bs = 122, .dw = 98, .ce = 12},
|
||||
{.bs = 45, .dw = 15, .ce = 15},
|
||||
{.bs = 43, .dw = 19, .ce = 12}
|
||||
{.bs = 73, .dw = 45, .ns = 7},
|
||||
{.bs = 122, .dw = 98, .ns = 5},
|
||||
{.bs = 45, .dw = 15, .ns = 3},
|
||||
{.bs = 43, .dw = 19, .ns = 15}
|
||||
}
|
||||
},
|
||||
{ /* Version 17 */
|
||||
.data_bytes = 815,
|
||||
.apat = {6, 30, 54, 78, 0},
|
||||
.ecc = {
|
||||
{.bs = 74, .dw = 46, .ce = 14},
|
||||
{.bs = 135, .dw = 107, .ce = 14},
|
||||
{.bs = 42, .dw = 14, .ce = 14},
|
||||
{.bs = 50, .dw = 22, .ce = 14}
|
||||
{.bs = 74, .dw = 46, .ns = 10},
|
||||
{.bs = 135, .dw = 107, .ns = 1},
|
||||
{.bs = 42, .dw = 14, .ns = 2},
|
||||
{.bs = 50, .dw = 22, .ns = 1}
|
||||
}
|
||||
},
|
||||
{ /* Version 18 */
|
||||
.data_bytes = 901,
|
||||
.apat = {6, 30, 56, 82, 0},
|
||||
.ecc = {
|
||||
{.bs = 69, .dw = 43, .ce = 13},
|
||||
{.bs = 150, .dw = 120, .ce = 15},
|
||||
{.bs = 42, .dw = 14, .ce = 14},
|
||||
{.bs = 50, .dw = 22, .ce = 14}
|
||||
{.bs = 69, .dw = 43, .ns = 9},
|
||||
{.bs = 150, .dw = 120, .ns = 5},
|
||||
{.bs = 42, .dw = 14, .ns = 2},
|
||||
{.bs = 50, .dw = 22, .ns = 17}
|
||||
}
|
||||
},
|
||||
{ /* Version 19 */
|
||||
.data_bytes = 991,
|
||||
.apat = {6, 30, 58, 86, 0},
|
||||
.ecc = {
|
||||
{.bs = 70, .dw = 44, .ce = 13},
|
||||
{.bs = 141, .dw = 113, .ce = 14},
|
||||
{.bs = 39, .dw = 13, .ce = 13},
|
||||
{.bs = 47, .dw = 21, .ce = 13}
|
||||
{.bs = 70, .dw = 44, .ns = 3},
|
||||
{.bs = 141, .dw = 113, .ns = 3},
|
||||
{.bs = 39, .dw = 13, .ns = 9},
|
||||
{.bs = 47, .dw = 21, .ns = 17}
|
||||
}
|
||||
},
|
||||
{ /* Version 20 */
|
||||
.data_bytes = 1085,
|
||||
.apat = {6, 34, 62, 90, 0},
|
||||
.ecc = {
|
||||
{.bs = 67, .dw = 41, .ce = 13},
|
||||
{.bs = 135, .dw = 107, .ce = 14},
|
||||
{.bs = 43, .dw = 15, .ce = 14},
|
||||
{.bs = 54, .dw = 24, .ce = 15}
|
||||
{.bs = 67, .dw = 41, .ns = 3},
|
||||
{.bs = 135, .dw = 107, .ns = 3},
|
||||
{.bs = 43, .dw = 15, .ns = 15},
|
||||
{.bs = 54, .dw = 24, .ns = 15}
|
||||
}
|
||||
},
|
||||
{ /* Version 21 */
|
||||
.data_bytes = 1156,
|
||||
.apat = {6, 28, 50, 72, 92, 0},
|
||||
.ecc = {
|
||||
{.bs = 68, .dw = 42, .ce = 13},
|
||||
{.bs = 144, .dw = 116, .ce = 14},
|
||||
{.bs = 46, .dw = 16, .ce = 15},
|
||||
{.bs = 50, .dw = 22, .ce = 14}
|
||||
{.bs = 68, .dw = 42, .ns = 17},
|
||||
{.bs = 144, .dw = 116, .ns = 4},
|
||||
{.bs = 46, .dw = 16, .ns = 19},
|
||||
{.bs = 50, .dw = 22, .ns = 17}
|
||||
}
|
||||
},
|
||||
{ /* Version 22 */
|
||||
.data_bytes = 1258,
|
||||
.apat = {6, 26, 50, 74, 98, 0},
|
||||
.ecc = {
|
||||
{.bs = 74, .dw = 46, .ce = 14},
|
||||
{.bs = 139, .dw = 111, .ce = 14},
|
||||
{.bs = 37, .dw = 13, .ce = 12},
|
||||
{.bs = 54, .dw = 24, .ce = 15}
|
||||
{.bs = 74, .dw = 46, .ns = 17},
|
||||
{.bs = 139, .dw = 111, .ns = 2},
|
||||
{.bs = 37, .dw = 13, .ns = 34},
|
||||
{.bs = 54, .dw = 24, .ns = 7}
|
||||
}
|
||||
},
|
||||
{ /* Version 23 */
|
||||
.data_bytes = 1364,
|
||||
.apat = {6, 30, 54, 78, 102, 0},
|
||||
.ecc = {
|
||||
{.bs = 75, .dw = 47, .ce = 14},
|
||||
{.bs = 151, .dw = 121, .ce = 15},
|
||||
{.bs = 45, .dw = 15, .ce = 15},
|
||||
{.bs = 54, .dw = 24, .ce = 15}
|
||||
{.bs = 75, .dw = 47, .ns = 4},
|
||||
{.bs = 151, .dw = 121, .ns = 4},
|
||||
{.bs = 45, .dw = 15, .ns = 16},
|
||||
{.bs = 54, .dw = 24, .ns = 11}
|
||||
}
|
||||
},
|
||||
{ /* Version 24 */
|
||||
.data_bytes = 1474,
|
||||
.apat = {6, 28, 54, 80, 106, 0},
|
||||
.ecc = {
|
||||
{.bs = 73, .dw = 45, .ce = 14},
|
||||
{.bs = 147, .dw = 117, .ce = 15},
|
||||
{.bs = 46, .dw = 16, .ce = 15},
|
||||
{.bs = 54, .dw = 24, .ce = 15}
|
||||
{.bs = 73, .dw = 45, .ns = 6},
|
||||
{.bs = 147, .dw = 117, .ns = 6},
|
||||
{.bs = 46, .dw = 16, .ns = 30},
|
||||
{.bs = 54, .dw = 24, .ns = 11}
|
||||
}
|
||||
},
|
||||
{ /* Version 25 */
|
||||
.data_bytes = 1588,
|
||||
.apat = {6, 32, 58, 84, 110, 0},
|
||||
.ecc = {
|
||||
{.bs = 75, .dw = 47, .ce = 14},
|
||||
{.bs = 132, .dw = 106, .ce = 13},
|
||||
{.bs = 45, .dw = 15, .ce = 15},
|
||||
{.bs = 54, .dw = 24, .ce = 15}
|
||||
{.bs = 75, .dw = 47, .ns = 8},
|
||||
{.bs = 132, .dw = 106, .ns = 8},
|
||||
{.bs = 45, .dw = 15, .ns = 22},
|
||||
{.bs = 54, .dw = 24, .ns = 7}
|
||||
}
|
||||
},
|
||||
{ /* Version 26 */
|
||||
.data_bytes = 1706,
|
||||
.apat = {6, 30, 58, 86, 114, 0},
|
||||
.ecc = {
|
||||
{.bs = 74, .dw = 46, .ce = 14},
|
||||
{.bs = 142, .dw = 114, .ce = 14},
|
||||
{.bs = 46, .dw = 16, .ce = 15},
|
||||
{.bs = 50, .dw = 22, .ce = 14}
|
||||
{.bs = 74, .dw = 46, .ns = 19},
|
||||
{.bs = 142, .dw = 114, .ns = 10},
|
||||
{.bs = 46, .dw = 16, .ns = 33},
|
||||
{.bs = 50, .dw = 22, .ns = 28}
|
||||
}
|
||||
},
|
||||
{ /* Version 27 */
|
||||
.data_bytes = 1828,
|
||||
.apat = {6, 34, 62, 90, 118, 0},
|
||||
.ecc = {
|
||||
{.bs = 73, .dw = 45, .ce = 14},
|
||||
{.bs = 152, .dw = 122, .ce = 15},
|
||||
{.bs = 45, .dw = 15, .ce = 15},
|
||||
{.bs = 53, .dw = 23, .ce = 15}
|
||||
{.bs = 73, .dw = 45, .ns = 22},
|
||||
{.bs = 152, .dw = 122, .ns = 8},
|
||||
{.bs = 45, .dw = 15, .ns = 12},
|
||||
{.bs = 53, .dw = 23, .ns = 8}
|
||||
}
|
||||
},
|
||||
{ /* Version 28 */
|
||||
.data_bytes = 1921,
|
||||
.apat = {6, 26, 50, 74, 98, 122, 0},
|
||||
.ecc = {
|
||||
{.bs = 73, .dw = 45, .ce = 14},
|
||||
{.bs = 147, .dw = 117, .ce = 15},
|
||||
{.bs = 45, .dw = 15, .ce = 15},
|
||||
{.bs = 54, .dw = 24, .ce = 15}
|
||||
{.bs = 73, .dw = 45, .ns = 3},
|
||||
{.bs = 147, .dw = 117, .ns = 3},
|
||||
{.bs = 45, .dw = 15, .ns = 11},
|
||||
{.bs = 54, .dw = 24, .ns = 4}
|
||||
}
|
||||
},
|
||||
{ /* Version 29 */
|
||||
.data_bytes = 2051,
|
||||
.apat = {6, 30, 54, 78, 102, 126, 0},
|
||||
.ecc = {
|
||||
{.bs = 73, .dw = 45, .ce = 14},
|
||||
{.bs = 146, .dw = 116, .ce = 15},
|
||||
{.bs = 45, .dw = 15, .ce = 15},
|
||||
{.bs = 73, .dw = 45, .ce = 14}
|
||||
{.bs = 73, .dw = 45, .ns = 21},
|
||||
{.bs = 146, .dw = 116, .ns = 7},
|
||||
{.bs = 45, .dw = 15, .ns = 19},
|
||||
{.bs = 53, .dw = 23, .ns = 1}
|
||||
}
|
||||
},
|
||||
{ /* Version 30 */
|
||||
.data_bytes = 2185,
|
||||
.apat = {6, 26, 52, 78, 104, 130, 0},
|
||||
.ecc = {
|
||||
{.bs = 75, .dw = 47, .ce = 14},
|
||||
{.bs = 145, .dw = 115, .ce = 15},
|
||||
{.bs = 45, .dw = 15, .ce = 15},
|
||||
{.bs = 54, .dw = 24, .ce = 15}
|
||||
{.bs = 75, .dw = 47, .ns = 19},
|
||||
{.bs = 145, .dw = 115, .ns = 5},
|
||||
{.bs = 45, .dw = 15, .ns = 23},
|
||||
{.bs = 54, .dw = 24, .ns = 15}
|
||||
}
|
||||
},
|
||||
{ /* Version 31 */
|
||||
.data_bytes = 2323,
|
||||
.apat = {6, 30, 56, 82, 108, 134, 0},
|
||||
.ecc = {
|
||||
{.bs = 74, .dw = 46, .ce = 14},
|
||||
{.bs = 145, .dw = 115, .ce = 15},
|
||||
{.bs = 45, .dw = 15, .ce = 15},
|
||||
{.bs = 54, .dw = 24, .ce = 15}
|
||||
{.bs = 74, .dw = 46, .ns = 2},
|
||||
{.bs = 145, .dw = 115, .ns = 13},
|
||||
{.bs = 45, .dw = 15, .ns = 23},
|
||||
{.bs = 54, .dw = 24, .ns = 42}
|
||||
}
|
||||
},
|
||||
{ /* Version 32 */
|
||||
.data_bytes = 2465,
|
||||
.apat = {6, 34, 60, 86, 112, 138, 0},
|
||||
.ecc = {
|
||||
{.bs = 74, .dw = 46, .ce = 14},
|
||||
{.bs = 145, .dw = 115, .ce = 15},
|
||||
{.bs = 45, .dw = 15, .ce = 15},
|
||||
{.bs = 54, .dw = 24, .ce = 15}
|
||||
{.bs = 74, .dw = 46, .ns = 10},
|
||||
{.bs = 145, .dw = 115, .ns = 17},
|
||||
{.bs = 45, .dw = 15, .ns = 19},
|
||||
{.bs = 54, .dw = 24, .ns = 10}
|
||||
}
|
||||
},
|
||||
{ /* Version 33 */
|
||||
.data_bytes = 2611,
|
||||
.apat = {6, 30, 58, 96, 114, 142, 0},
|
||||
.apat = {6, 30, 58, 86, 114, 142, 0},
|
||||
.ecc = {
|
||||
{.bs = 74, .dw = 46, .ce = 14},
|
||||
{.bs = 145, .dw = 115, .ce = 15},
|
||||
{.bs = 45, .dw = 15, .ce = 15},
|
||||
{.bs = 54, .dw = 24, .ce = 15}
|
||||
{.bs = 74, .dw = 46, .ns = 14},
|
||||
{.bs = 145, .dw = 115, .ns = 17},
|
||||
{.bs = 45, .dw = 15, .ns = 11},
|
||||
{.bs = 54, .dw = 24, .ns = 29}
|
||||
}
|
||||
},
|
||||
{ /* Version 34 */
|
||||
.data_bytes = 2761,
|
||||
.apat = {6, 34, 62, 90, 118, 146, 0},
|
||||
.ecc = {
|
||||
{.bs = 74, .dw = 46, .ce = 14},
|
||||
{.bs = 145, .dw = 115, .ce = 15},
|
||||
{.bs = 46, .dw = 16, .ce = 15},
|
||||
{.bs = 54, .dw = 24, .ce = 15}
|
||||
{.bs = 74, .dw = 46, .ns = 14},
|
||||
{.bs = 145, .dw = 115, .ns = 13},
|
||||
{.bs = 46, .dw = 16, .ns = 59},
|
||||
{.bs = 54, .dw = 24, .ns = 44}
|
||||
}
|
||||
},
|
||||
{ /* Version 35 */
|
||||
.data_bytes = 2876,
|
||||
.apat = {6, 30, 54, 78, 102, 126, 150},
|
||||
.ecc = {
|
||||
{.bs = 75, .dw = 47, .ce = 14},
|
||||
{.bs = 151, .dw = 121, .ce = 15},
|
||||
{.bs = 45, .dw = 15, .ce = 15},
|
||||
{.bs = 54, .dw = 24, .ce = 15}
|
||||
{.bs = 75, .dw = 47, .ns = 12},
|
||||
{.bs = 151, .dw = 121, .ns = 12},
|
||||
{.bs = 45, .dw = 15, .ns = 22},
|
||||
{.bs = 54, .dw = 24, .ns = 39}
|
||||
}
|
||||
},
|
||||
{ /* Version 36 */
|
||||
.data_bytes = 3034,
|
||||
.apat = {6, 24, 50, 76, 102, 128, 154},
|
||||
.ecc = {
|
||||
{.bs = 75, .dw = 47, .ce = 14},
|
||||
{.bs = 151, .dw = 121, .ce = 15},
|
||||
{.bs = 45, .dw = 15, .ce = 15},
|
||||
{.bs = 54, .dw = 24, .ce = 15}
|
||||
{.bs = 75, .dw = 47, .ns = 6},
|
||||
{.bs = 151, .dw = 121, .ns = 6},
|
||||
{.bs = 45, .dw = 15, .ns = 2},
|
||||
{.bs = 54, .dw = 24, .ns = 46}
|
||||
}
|
||||
},
|
||||
{ /* Version 37 */
|
||||
.data_bytes = 3196,
|
||||
.apat = {6, 28, 54, 80, 106, 132, 158},
|
||||
.ecc = {
|
||||
{.bs = 74, .dw = 46, .ce = 14},
|
||||
{.bs = 152, .dw = 122, .ce = 15},
|
||||
{.bs = 45, .dw = 15, .ce = 15},
|
||||
{.bs = 54, .dw = 24, .ce = 15}
|
||||
{.bs = 74, .dw = 46, .ns = 29},
|
||||
{.bs = 152, .dw = 122, .ns = 17},
|
||||
{.bs = 45, .dw = 15, .ns = 24},
|
||||
{.bs = 54, .dw = 24, .ns = 49}
|
||||
}
|
||||
},
|
||||
{ /* Version 38 */
|
||||
.data_bytes = 3362,
|
||||
.apat = {6, 32, 58, 84, 110, 136, 162},
|
||||
.ecc = {
|
||||
{.bs = 74, .dw = 46, .ce = 14},
|
||||
{.bs = 152, .dw = 122, .ce = 15},
|
||||
{.bs = 45, .dw = 15, .ce = 15},
|
||||
{.bs = 54, .dw = 24, .ce = 15}
|
||||
{.bs = 74, .dw = 46, .ns = 13},
|
||||
{.bs = 152, .dw = 122, .ns = 4},
|
||||
{.bs = 45, .dw = 15, .ns = 42},
|
||||
{.bs = 54, .dw = 24, .ns = 48}
|
||||
}
|
||||
},
|
||||
{ /* Version 39 */
|
||||
.data_bytes = 3532,
|
||||
.apat = {6, 26, 54, 82, 110, 138, 166},
|
||||
.ecc = {
|
||||
{.bs = 75, .dw = 47, .ce = 14},
|
||||
{.bs = 147, .dw = 117, .ce = 15},
|
||||
{.bs = 45, .dw = 15, .ce = 15},
|
||||
{.bs = 54, .dw = 24, .ce = 15}
|
||||
{.bs = 75, .dw = 47, .ns = 40},
|
||||
{.bs = 147, .dw = 117, .ns = 20},
|
||||
{.bs = 45, .dw = 15, .ns = 10},
|
||||
{.bs = 54, .dw = 24, .ns = 43}
|
||||
}
|
||||
},
|
||||
{ /* Version 40 */
|
||||
.data_bytes = 3706,
|
||||
.apat = {6, 30, 58, 86, 114, 142, 170},
|
||||
.ecc = {
|
||||
{.bs = 75, .dw = 47, .ce = 14},
|
||||
{.bs = 148, .dw = 118, .ce = 15},
|
||||
{.bs = 45, .dw = 15, .ce = 15},
|
||||
{.bs = 54, .dw = 24, .ce = 15}
|
||||
{.bs = 75, .dw = 47, .ns = 18},
|
||||
{.bs = 148, .dw = 118, .ns = 19},
|
||||
{.bs = 45, .dw = 15, .ns = 20},
|
||||
{.bs = 54, .dw = 24, .ns = 34}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
826
source/remote.c
826
source/remote.c
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Anemone3DS
|
||||
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
|
||||
* Copyright (C) 2016-2020 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
|
||||
|
||||
376
source/themes.c
376
source/themes.c
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Anemone3DS
|
||||
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
|
||||
* Copyright (C) 2016-2020 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,16 +184,30 @@ 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);
|
||||
}
|
||||
|
||||
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 (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);
|
||||
}
|
||||
|
||||
if(R_FAILED(res)) return res;
|
||||
} else
|
||||
@@ -247,18 +261,24 @@ 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);
|
||||
savedata->shuffle = (installmode & THEME_INSTALL_SHUFFLE) ? 1 : 0;
|
||||
memset(savedata->shuffle_themes, 0, sizeof(ThemeEntry_s)*MAX_SHUFFLE_THEMES);
|
||||
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);
|
||||
@@ -301,6 +321,344 @@ 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(®ionCode);
|
||||
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;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of Anemone3DS
|
||||
* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md
|
||||
* Copyright (C) 2016-2020 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
|
||||
|
||||
Reference in New Issue
Block a user