Major rewrite: less repetition (#101)

* first step of rewriting: at least it compiles™

* fix tabs/spaces

* fix dabort on load

* fix preview crash

* sorting isnt done outside of loading

* step one of making remake_file useless

* camera/qr code cleanup

* fix dabort when folder is empty, and bring back preview optimization

* fix button for switching modes and show mode when folder is empty

* fix scanning qrs

* no more splash discrimination

* add debug helpers

* fix theme installing
turns out that wasnt such a good idea

* fix battery icon

* mistake

* themeplaza compatibility

* make use of load_data

* don't drink and copy-paste, kids

* fix user-agent

* remove useless

* cleanup includes

* not even used

* add splash buttons

* upgrade buttons drawing

* fix controls while preview is up

* improve positions
This commit is contained in:
LiquidFenrir
2017-12-02 16:09:38 +01:00
committed by Alex Taber
parent ab4ead03b5
commit d937ae1716
15 changed files with 803 additions and 1149 deletions

View File

@@ -55,7 +55,7 @@ CFLAGS := -g -Wall -Wextra -O2 -mword-relocations \
revision := $(shell git describe --tags --match v[0-9]* --abbrev=8 | sed 's/-[0-9]*-g/-/')
CFLAGS += $(INCLUDE) -DARM11 -D_3DS -D_GNU_SOURCE -DVERSION="\"$(revision)\""
CFLAGS += $(INCLUDE) -DARM11 -D_3DS -D_GNU_SOURCE -DVERSION="\"$(revision)\"" -DUSER_AGENT="\"$(APP_TITLE)/$(revision)\""
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11

View File

@@ -29,11 +29,10 @@
#include "common.h"
bool qr_mode;
void init_qr(void);
void exit_qr(void);
void take_picture(void);
Result http_get(char *url, char *path);
bool scan_qr(EntryMode current_mode);
Result http_get(char *url, const char *path);
#endif

View File

@@ -33,25 +33,26 @@
#include <stdlib.h>
#include <string.h>
#define THEMES_PATH "/Themes/"
#define SPLASHES_PATH "/Splashes/"
#define ENTRIES_PER_SCREEN 4
#define SINGLE_INSTALL 0
#define SHUFFLE_INSTALL 1
#define BGM_INSTALL 2
#define UNINSTALL 3
#define DOWNLOADING 3
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define POS() DEBUG("%s (line %d)...\n", __func__, __LINE__)
#define ERROR 0
#define WARNING 1
static const int THEMES_PER_SCREEN = 4;
#define DEBUGPOS(...) \
POS(); \
DEBUG(__VA_ARGS__)
bool homebrew;
bool splash_mode;
int shuffle_theme_count;
typedef enum {
MODE_THEMES = 0,
MODE_SPLASHES,
MODE_AMOUNT,
} EntryMode;
extern const char * main_paths[MODE_AMOUNT];
enum TextureID {
TEXTURE_FONT_RESERVED = 0, //used by pp2d for the font
TEXTURE_FONT_RESERVED = 0, // used by pp2d for the font
TEXTURE_ARROW,
TEXTURE_SHUFFLE,
TEXTURE_BATTERY_1,
@@ -62,6 +63,10 @@ enum TextureID {
TEXTURE_BATTERY_CHARGE,
TEXTURE_QR,
TEXTURE_PREVIEW,
TEXTURE_ICON, // always the last
};
void exit_function(void);
#endif

View File

@@ -27,18 +27,49 @@
#ifndef DRAW_H
#define DRAW_H
#include "themes.h"
#include "splashes.h"
#include "camera.h"
#include "common.h"
#include "loading.h"
typedef enum {
INSTALL_SPLASH,
INSTALL_SPLASH_DELETE,
INSTALL_SINGLE,
INSTALL_SHUFFLE,
INSTALL_BGM,
INSTALL_DOWNLOAD,
} InstallType;
typedef enum {
ERROR_LEVEL_ERROR,
ERROR_LEVEL_WARNING,
} ErrorLevel;
#define BUTTONS_START_Y 140
#define BUTTONS_STEP 25
enum {
BUTTONS_Y_PREVIEW = BUTTONS_START_Y+5,
BUTTONS_Y_LINE_1 = BUTTONS_START_Y + BUTTONS_STEP*1,
BUTTONS_Y_LINE_2 = BUTTONS_START_Y + BUTTONS_STEP*2,
BUTTONS_Y_LINE_3 = BUTTONS_START_Y + BUTTONS_STEP*3,
BUTTONS_X_LEFT = 20,
BUTTONS_X_RIGHT = 200,
} ButtonPos;
void init_screens(void);
void exit_screens(void);
void draw_themext_error(void);
void draw_base_interface(void);
void draw_theme_install(int install_type);
void draw_theme_interface(Theme_s * themes_list, int theme_count, int selected_theme, bool preview_mode, int shuffle_theme_count);
void draw_splash_install(int install_type);
void draw_splash_interface(Splash_s *splashes_list, int splash_count, int selected_splash, bool preview_mode);
void throw_error(char* error, int error_type);
void throw_error(char* error, ErrorLevel level);
void draw_preview(int preview_offset);
void draw_install(InstallType type);
void draw_interface(Entry_List_s* list, EntryMode current_mode);
#endif

View File

@@ -36,7 +36,7 @@ FS_Archive ArchiveThemeExt;
Result open_archives(void);
Result close_archives(void);
u64 file_to_buf(FS_Path path, FS_Archive archive, char** buf);
u32 file_to_buf(FS_Path path, FS_Archive archive, char** buf);
u32 zip_file_to_buf(char *file_name, u16 *zip_path, char **buf);
Result buf_to_file(u32 size, char *path, FS_Archive archive, char *buf);

60
include/loading.h Normal file
View File

@@ -0,0 +1,60 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
*
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#ifndef LOADING_H
#define LOADING_H
#include "common.h"
typedef struct {
u16 name[0x41];
u16 desc[0x81];
u16 author[0x41];
u32 placeholder_color;
ssize_t icon_id;
u16 path[0x106];
bool is_zip;
bool in_shuffle;
} Entry_s;
typedef struct {
Entry_s * entries;
int entries_count;
int scroll;
int selected_entry;
int shuffle_count;
} Entry_List_s;
Result load_entries(const char * loading_path, Entry_List_s * list);
bool load_preview(Entry_List_s list, int * preview_offset);
u32 load_data(char * filename, Entry_s entry, char ** buf);
#endif

View File

@@ -28,26 +28,9 @@
#define SPLASHES_H
#include "common.h"
#include "loading.h"
typedef struct {
u16 name[0x41];
u16 desc[0x81];
u16 author[0x41];
u32 placeholder_color;
ssize_t icon_id;
u16 path[0x106];
bool is_zip;
} Splash_s;
Splash_s *splashes_list;
int splash_count;
Result get_splashes(Splash_s** splashes_list, int *splash_count);
int splashcmp(const void* a, const void* b);
void splash_install(Splash_s splash_to_install);
void splash_delete();
void load_splash_preview(Splash_s *splash);
void splash_delete(void);
void splash_install(Entry_s splash);
#endif

View File

@@ -28,33 +28,12 @@
#define THEMES_H
#include "common.h"
#include "loading.h"
typedef struct {
u16 name[0x41];
u16 desc[0x81];
u16 author[0x41];
void delete_theme(Entry_s theme);
Result theme_install(Entry_s theme);
u32 placeholder_color;
ssize_t icon_id;
bool has_preview;
int preview_offset;
u16 path[0x106];
bool is_zip;
bool in_shuffle;
} Theme_s;
Theme_s * themes_list;
int theme_count;
void load_theme_preview(Theme_s *theme);
Result get_themes(Theme_s **themes_list, int *theme_count);
int themecmp(const void* a, const void* b);
void del_theme(u16 *path);
Result single_install(Theme_s theme);
Result shuffle_install(Theme_s *themes_list, int theme_count);
Result bgm_install(Theme_s bgm_to_install);
Result shuffle_install(Entry_s* themes_list, int themes_count);
Result bgm_install(Entry_s bgm_to_install);
#endif

View File

@@ -27,14 +27,16 @@
#include "camera.h"
#include "quirc/quirc.h"
#include "draw.h"
#include "fs.h"
#include "themes.h"
#include "pp2d/pp2d/pp2d.h"
u32 transfer_size;
Handle event;
struct quirc* context;
#include "draw.h"
#include "fs.h"
#include "loading.h"
static u32 transfer_size;
static Handle event;
static struct quirc* context;
static u16 * camera_buf = NULL;
void init_qr(void)
{
@@ -69,10 +71,14 @@ void exit_qr(void)
CAMU_Activate(SELECT_NONE);
camExit();
quirc_destroy(context);
free(camera_buf);
camera_buf = NULL;
}
void scan_qr(u16 *buf)
bool scan_qr(EntryMode current_mode)
{
if(camera_buf == NULL) return false;
int w;
int h;
@@ -82,7 +88,7 @@ void scan_qr(u16 *buf)
{
for (ssize_t y = 0; y < h; y++)
{
u16 px = buf[y * 400 + x];
u16 px = camera_buf[y * 400 + x];
image[y * w + x] = (u8)(((((px >> 11) & 0x1F) << 3) + (((px >> 5) & 0x3F) << 2) + ((px & 0x1F) << 3)) / 3);
}
}
@@ -96,59 +102,49 @@ void scan_qr(u16 *buf)
quirc_extract(context, 0, &code);
if (!quirc_decode(&code, &data))
{
qr_mode = false;
http_get((char*)data.payload, splash_mode ? "/Splashes/" : "/Themes/");
http_get((char*)data.payload, main_paths[current_mode]);
exit_qr();
return true;
}
}
return false;
}
void take_picture(void)
{
u16 *buf = malloc(sizeof(u16) * 400 * 240 * 4);
if (buf == NULL) return;
CAMU_SetReceiving(&event, buf, PORT_CAM1, 240 * 400 * 2, transfer_size);
pp2d_begin_draw(GFX_TOP, GFX_LEFT);
free(camera_buf);
camera_buf = malloc(sizeof(u16) * 400 * 240 * 4);
if (camera_buf == NULL) return;
CAMU_SetReceiving(&event, camera_buf, PORT_CAM1, 240 * 400 * 2, transfer_size);
svcWaitSynchronization(event, U64_MAX);
svcCloseHandle(event);
pp2d_begin_draw(GFX_TOP, GFX_LEFT);
u32 *rgba8_buf = malloc(240 * 400 * sizeof(u32));
if (rgba8_buf == NULL) return;
for (int i = 0; i < 240 * 400; i++)
{
rgba8_buf[i] = RGB565_TO_ABGR8(buf[i]);
rgba8_buf[i] = RGB565_TO_ABGR8(camera_buf[i]);
}
pp2d_free_texture(TEXTURE_QR);
pp2d_load_texture_memory(TEXTURE_QR, rgba8_buf, 400, 240);
free(rgba8_buf);
pp2d_draw_texture(TEXTURE_QR, 0, 0);
pp2d_draw_rectangle(0, 216, 400, 24, RGBA8(55, 122, 168, 255));
pp2d_draw_text_center(GFX_TOP, 220, 0.5, 0.5, RGBA8(255, 255, 255, 255), "Press \uE005 To Quit");
pp2d_draw_rectangle(0, 0, 400, 24, RGBA8(55, 122, 168, 255));
pp2d_draw_text_center(GFX_TOP, 4, 0.5, 0.5, RGBA8(255, 255, 255, 255), "Press \uE004 To Scan");
pp2d_end_draw();
free(rgba8_buf);
pp2d_free_texture(TEXTURE_QR);
hidScanInput();
u32 kDown = hidKeysDown();
if (kDown & KEY_L)
{
CAMU_StopCapture(PORT_BOTH);
CAMU_Activate(SELECT_NONE);
scan_qr(buf);
CAMU_Activate(SELECT_OUT1_OUT2);
CAMU_StartCapture(PORT_BOTH);
}
if (kDown & KEY_R)
{
exit_qr();
qr_mode = false;
}
free(buf);
}
/*
Putting this in camera because I'm too lazy to make a network.c
This'll probably get refactored later
*/
Result http_get(char *url, char *path)
Result http_get(char *url, const char *path)
{
Result ret;
httpcContext context;
@@ -164,9 +160,10 @@ Result http_get(char *url, char *path)
ret = httpcOpenContext(&context, HTTPC_METHOD_GET, url, 1);
ret = httpcSetSSLOpt(&context, SSLCOPT_DisableVerify); // should let us do https
ret = httpcSetKeepAlive(&context, HTTPC_KEEPALIVE_ENABLED);
ret = httpcAddRequestHeaderField(&context, "User-Agent", "Anemone3DS/1.1.0");
ret = httpcAddRequestHeaderField(&context, "User-Agent", USER_AGENT);
ret = httpcAddRequestHeaderField(&context, "Connection", "Keep-Alive");
draw_theme_install(DOWNLOADING);
draw_install(INSTALL_DOWNLOAD);
ret = httpcBeginRequest(&context);
if (ret != 0)
{
@@ -234,9 +231,7 @@ Result http_get(char *url, char *path)
free(content_disposition);
free(new_url);
free(buf);
char error[29] = {0};
sprintf(error, "Target is not a valid %s", splash_mode ? "splash" : "theme");
throw_error(error, WARNING);
throw_error("Target is not valid!", ERROR_LEVEL_WARNING);
return -1;
}
for (size_t i = 0; i < strlen(filename); i++)
@@ -283,14 +278,13 @@ Result http_get(char *url, char *path)
char path_to_file[0x106] = {0};
strcpy(path_to_file, path);
strcat(path_to_file, filename);
char * extension = strrchr(path_to_file, '.');
if (extension == NULL || strcmp(extension, ".zip"))
strcat(path_to_file, ".zip");
remake_file(path_to_file, ArchiveSD, size);
buf_to_file(size, path_to_file, ArchiveSD, (char*)buf);
if (splash_mode) get_splashes(&splashes_list, &splash_count);
else get_themes(&themes_list, &theme_count);
exit_qr();
free(content_disposition);
free(new_url);
free(buf);

View File

@@ -25,14 +25,12 @@
*/
#include "draw.h"
#include "common.h"
#include "unicode.h"
#include "pp2d/pp2d/pp2d.h"
#include "quirc/quirc.h"
#include <time.h>
enum Colors {
COLOR_BACKGROUND = ABGR8(255, 32, 28, 35), //silver-y black
COLOR_ACCENT = RGBA8(55, 122, 168, 255),
@@ -65,11 +63,7 @@ void exit_screens(void)
pp2d_exit();
}
static int theme_vertical_scroll = 0;
static int splash_vertical_scroll = 0;
void draw_base_interface(void)
static void draw_base_interface(void)
{
pp2d_begin_draw(GFX_TOP, GFX_LEFT);
pp2d_draw_rectangle(0, 0, 400, 23, COLOR_ACCENT);
@@ -78,29 +72,32 @@ void draw_base_interface(void)
struct tm tm = *localtime(&t);
pp2d_draw_textf(7, 2, 0.6, 0.6, COLOR_WHITE, "%.2i", tm.tm_hour);
pp2d_draw_text(28, 2, 0.6, 0.6, COLOR_WHITE, (tm.tm_sec % 2 == 1) ? ":" : " ");
pp2d_draw_text(28, 1, 0.6, 0.6, COLOR_WHITE, (tm.tm_sec % 2 == 1) ? ":" : " ");
pp2d_draw_textf(34, 2, 0.6, 0.6, COLOR_WHITE, "%.2i", tm.tm_min);
u8 battery_charging;
u8 battery_charging = 0;
PTMU_GetBatteryChargeState(&battery_charging);
u8 battery_status;
u8 battery_status = 0;
PTMU_GetBatteryLevel(&battery_status);
pp2d_draw_texture(2 + battery_status, 357, 2);
pp2d_draw_texture(TEXTURE_BATTERY_1 + battery_status - 1, 357, 2);
if (battery_charging)
if(battery_charging)
pp2d_draw_texture(TEXTURE_BATTERY_CHARGE, 357, 2);
pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
pp2d_draw_rectangle(0, 0, 320, 24, COLOR_ACCENT);
pp2d_draw_rectangle(0, 216, 320, 24, COLOR_ACCENT);
pp2d_draw_text(7, 219, 0.6, 0.6, COLOR_WHITE, VERSION);
pp2d_draw_on(GFX_TOP, GFX_LEFT);
}
void throw_error(char* error, int error_type) {
switch (error_type) {
case ERROR:
while (aptMainLoop())
void throw_error(char* error, ErrorLevel level)
{
switch(level)
{
case ERROR_LEVEL_ERROR:
while(aptMainLoop())
{
hidScanInput();
u32 kDown = hidKeysDown();
@@ -108,254 +105,190 @@ void throw_error(char* error, int error_type) {
pp2d_draw_text_center(GFX_TOP, 120, 0.6, 0.6, COLOR_RED, error);
pp2d_draw_wtext_center(GFX_TOP, 150, 0.6, 0.6, COLOR_WHITE, L"Press \uE000 to shut down.");
pp2d_end_draw();
if (kDown & KEY_A) {
if (homebrew)
APT_HardwareResetAsync();
else {
srvPublishToSubscriber(0x202, 0);
}
}
if(kDown & KEY_A) exit_function();
}
break;
case ERROR_LEVEL_WARNING:
while(aptMainLoop())
{
hidScanInput();
u32 kDown = hidKeysDown();
draw_base_interface();
pp2d_draw_text_center(GFX_TOP, 120, 0.6, 0.6, COLOR_YELLOW, error);
pp2d_draw_wtext_center(GFX_TOP, 150, 0.6, 0.6, COLOR_WHITE, L"Press \uE000 to continue.");
pp2d_end_draw();
if(kDown & KEY_A) break;
}
case WARNING:
while (aptMainLoop())
{
hidScanInput();
u32 kDown = hidKeysDown();
draw_base_interface();
pp2d_draw_text_center(GFX_TOP, 120, 0.6, 0.6, COLOR_YELLOW, error);
pp2d_draw_wtext_center(GFX_TOP, 150, 0.6, 0.6, COLOR_WHITE, L"Press \uE000 to continue.");
pp2d_end_draw();
if (kDown & KEY_A) break;
}
break;
}
pp2d_end_draw();
}
void draw_theme_install(int install_type)
void draw_preview(int preview_offset)
{
pp2d_begin_draw(GFX_TOP, GFX_LEFT);
pp2d_draw_texture_part(TEXTURE_PREVIEW, 0, 0, preview_offset, 0, 400, 240);
pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
pp2d_draw_texture_part(TEXTURE_PREVIEW, 0, 0, 40 + preview_offset, 240, 320, 240);
}
void draw_install(InstallType type)
{
draw_base_interface();
switch(install_type)
switch(type)
{
case 0:
case INSTALL_SINGLE:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Installing a single theme...");
break;
case 1:
case INSTALL_SHUFFLE:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Installing a shuffle theme...");
break;
case 2:
case INSTALL_BGM:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Installing BGM...");
break;
case 3:
case INSTALL_DOWNLOAD:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Downloading...");
break;
case INSTALL_SPLASH:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Installing a splash...");
break;
case INSTALL_SPLASH_DELETE:
pp2d_draw_text_center(GFX_TOP, 120, 0.8, 0.8, COLOR_WHITE, "Uninstalling a splash...");
break;
default:
break;
}
pp2d_end_draw();
}
void draw_theme_interface(Theme_s * themes_list, int theme_count, int selected_theme, bool preview_mode, int shuffle_theme_count)
{
if (themes_list == NULL)
{
draw_base_interface();
pp2d_draw_text_center(GFX_TOP, 80, 0.7, 0.7, COLOR_YELLOW, "No themes found!");
pp2d_draw_text_center(GFX_TOP, 110, 0.7, 0.7, COLOR_YELLOW, "Press \uE005 to download from QR");
pp2d_draw_text_center(GFX_TOP, 140, 0.7, 0.7, COLOR_YELLOW, "Or \uE045 to quit");
pp2d_end_draw();
return;
}
Theme_s current_theme = themes_list[selected_theme];
if (preview_mode)
{
if (current_theme.has_preview)
{
pp2d_begin_draw(GFX_TOP, GFX_LEFT);
pp2d_draw_texture_part(TEXTURE_PREVIEW, 0, 0, current_theme.preview_offset, 0, 400, 240);
pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
pp2d_draw_texture_part(TEXTURE_PREVIEW, 0, 0, 40+current_theme.preview_offset, 240, 320, 240);
}
}
else
{
draw_base_interface();
pp2d_draw_text_center(GFX_TOP, 4, 0.5, 0.5, COLOR_WHITE, "Theme mode");
wchar_t title[0x41] = {0};
utf16_to_utf32((u32*)title, current_theme.name, 0x40);
pp2d_draw_wtext_wrap(20, 30, 0.7, 0.7, COLOR_WHITE, 380, title);
wchar_t author[0x41] = {0};
utf16_to_utf32((u32*)author, current_theme.author, 0x40);
pp2d_draw_text(20, 50, 0.5, 0.5, COLOR_WHITE, "By: ");
pp2d_draw_wtext_wrap(44, 50, 0.5, 0.5, COLOR_WHITE, 380, author);
wchar_t description[0x81] = {0};
utf16_to_utf32((u32*)description, current_theme.desc, 0x80);
pp2d_draw_wtext_wrap(20, 65, 0.5, 0.5, COLOR_WHITE, 363, description);
pp2d_draw_wtext(20, 150, 0.6, 0.6, COLOR_WHITE, L"\uE046 Install Shuffle Theme");
pp2d_draw_wtext(200, 150, 0.6, 0.6, COLOR_WHITE, L"\uE004 Switch to Splashes");
pp2d_draw_wtext(20, 180, 0.6, 0.6, COLOR_WHITE, L"\uE000 Install Theme");
pp2d_draw_wtext(200, 180, 0.6, 0.6, COLOR_WHITE, L"\uE001 Queue Shuffle");
pp2d_draw_wtext(20, 210, 0.6, 0.6, COLOR_WHITE, L"\uE002 Install BGM");
pp2d_draw_wtext(200, 210, 0.6, 0.6, COLOR_WHITE, L"\uE003 Preview Theme");
pp2d_draw_wtext(130, 120, 0.6, 0.6, COLOR_WHITE, L"\uE005 Scan QRCode");
pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
pp2d_draw_textf(7, 3, 0.6, 0.6, COLOR_WHITE, "Selected: %i/10", shuffle_theme_count);
// Scroll the menu up or down if the selected theme is out of its bounds
//----------------------------------------------------------------
for (int i = 0; i < theme_count; i++) {
if (theme_count <= THEMES_PER_SCREEN)
break;
if (theme_vertical_scroll > selected_theme)
theme_vertical_scroll--;
if ((i < selected_theme) && \
((selected_theme - theme_vertical_scroll) >= THEMES_PER_SCREEN) && \
(theme_vertical_scroll != ( - THEMES_PER_SCREEN)))
theme_vertical_scroll++;
}
//----------------------------------------------------------------
// Show arrows if there are themes out of bounds
//----------------------------------------------------------------
if (theme_vertical_scroll > 0)
pp2d_draw_texture(TEXTURE_ARROW, 155, 6);
if (theme_vertical_scroll + THEMES_PER_SCREEN < theme_count)
pp2d_draw_texture_flip(TEXTURE_ARROW, 155, 224, VERTICAL);
for (int i = theme_vertical_scroll; i < (THEMES_PER_SCREEN + theme_vertical_scroll); i++)
{
if (i >= theme_count)
break;
current_theme = themes_list[i];
wchar_t name[0x80] = {0};
utf16_to_utf32((u32*)name, current_theme.name, 0x80);
int vertical_offset = 48 * (i-theme_vertical_scroll);
u32 font_color = COLOR_WHITE;
if (i == selected_theme)
{
font_color = COLOR_BLACK;
pp2d_draw_rectangle(0, 24 + vertical_offset, 320, 48, COLOR_CURSOR);
}
pp2d_draw_wtext(54, 40 + vertical_offset, 0.55, 0.55, font_color, name);
if (!current_theme.placeholder_color)
pp2d_draw_texture(current_theme.icon_id, 0, 24 + vertical_offset);
else
pp2d_draw_rectangle(0, 24 + vertical_offset, 48, 48, current_theme.placeholder_color);
if (current_theme.in_shuffle)
pp2d_draw_texture_blend(TEXTURE_SHUFFLE, 280, 32 + vertical_offset, font_color);
}
}
pp2d_end_draw();
}
void draw_splash_install(int install_type)
void draw_interface(Entry_List_s* list, EntryMode current_mode)
{
draw_base_interface();
switch (install_type)
const char* mode_string[] = {
"Theme mode",
"Splashes mode",
};
pp2d_draw_text_center(GFX_TOP, 4, 0.5, 0.5, COLOR_WHITE, mode_string[current_mode]);
if(list->entries == NULL)
{
case SINGLE_INSTALL:
pp2d_draw_text_center(GFX_TOP, 110, 0.7, 0.7, COLOR_WHITE, "Installing a splash...");
const char* mode_found_string[] = {
"No themes found",
"No splashes found",
};
pp2d_draw_text_center(GFX_TOP, 80, 0.7, 0.7, COLOR_YELLOW, mode_found_string[current_mode]);
pp2d_draw_text_center(GFX_TOP, 110, 0.7, 0.7, COLOR_YELLOW, "Press \uE005 to download from QR");
const char* mode_switch_string[] = {
"Or \uE004 to switch to splashes",
"Or \uE004 to switch to themes",
};
pp2d_draw_text_center(GFX_TOP, 140, 0.7, 0.7, COLOR_YELLOW, mode_switch_string[current_mode]);
pp2d_draw_text_center(GFX_TOP, 170, 0.7, 0.7, COLOR_YELLOW, "Or \uE045 to quit");
return;
}
int selected_entry = list->selected_entry;
Entry_s current_entry = list->entries[selected_entry];
wchar_t title[0x41] = {0};
utf16_to_utf32((u32*)title, current_entry.name, 0x40);
pp2d_draw_wtext_wrap(20, 30, 0.7, 0.7, COLOR_WHITE, 380, title);
wchar_t author[0x41] = {0};
utf16_to_utf32((u32*)author, current_entry.author, 0x40);
pp2d_draw_text(20, 50, 0.5, 0.5, COLOR_WHITE, "By: ");
pp2d_draw_wtext_wrap(44, 50, 0.5, 0.5, COLOR_WHITE, 380, author);
wchar_t description[0x81] = {0};
utf16_to_utf32((u32*)description, current_entry.desc, 0x80);
pp2d_draw_wtext_wrap(20, 65, 0.5, 0.5, COLOR_WHITE, 363, description);
switch(current_mode)
{
case MODE_THEMES:
pp2d_draw_text_center(GFX_TOP, BUTTONS_Y_PREVIEW, 0.6, 0.6, COLOR_WHITE, "\uE003 Preview Theme");
pp2d_draw_wtext(BUTTONS_X_LEFT, BUTTONS_Y_LINE_1, 0.6, 0.6, COLOR_WHITE, L"\uE004 Switch to Splashes");
pp2d_draw_wtext(BUTTONS_X_RIGHT, BUTTONS_Y_LINE_1, 0.6, 0.6, COLOR_WHITE, L"\uE005 Scan QRCode");
pp2d_draw_wtext(BUTTONS_X_LEFT, BUTTONS_Y_LINE_2, 0.6, 0.6, COLOR_WHITE, L"\uE000 Install Theme");
pp2d_draw_wtext(BUTTONS_X_RIGHT, BUTTONS_Y_LINE_2, 0.6, 0.6, COLOR_WHITE, L"\uE001 Queue Shuffle");
pp2d_draw_wtext(BUTTONS_X_LEFT, BUTTONS_Y_LINE_3, 0.6, 0.6, COLOR_WHITE, L"\uE002 Install BGM");
pp2d_draw_wtext(BUTTONS_X_RIGHT, BUTTONS_Y_LINE_3, 0.6, 0.6, COLOR_WHITE, L"\uE046 Install Shuffle Themes");
break;
case UNINSTALL:
pp2d_draw_text_center(GFX_TOP, 110, 0.7, 0.7, COLOR_WHITE, "Uninstalling a splash...");
case MODE_SPLASHES:
pp2d_draw_text_center(GFX_TOP, BUTTONS_Y_PREVIEW, 0.6, 0.6, COLOR_WHITE, "\uE003 Preview Splash");
pp2d_draw_wtext(BUTTONS_X_LEFT, BUTTONS_Y_LINE_1, 0.6, 0.6, COLOR_WHITE, L"\uE004 Switch to Themes");
pp2d_draw_wtext(BUTTONS_X_RIGHT, BUTTONS_Y_LINE_1, 0.6, 0.6, COLOR_WHITE, L"\uE005 Scan QRCode");
pp2d_draw_wtext(BUTTONS_X_LEFT, BUTTONS_Y_LINE_2, 0.6, 0.6, COLOR_WHITE, L"\uE000 Install Splash");
pp2d_draw_wtext(BUTTONS_X_RIGHT, BUTTONS_Y_LINE_2, 0.6, 0.6, COLOR_WHITE, L"\uE001 Delete installed Splash");
break;
default:
break;
}
pp2d_end_draw();
}
void draw_splash_interface(Splash_s *splashes_list, int splash_count, int selected_splash, bool preview_mode)
{
if (splashes_list == NULL)
pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
switch(current_mode)
{
draw_base_interface();
pp2d_draw_text_center(GFX_TOP, 80, 0.7, 0.7, COLOR_YELLOW, "No splashes found!");
pp2d_draw_text_center(GFX_TOP, 110, 0.7, 0.7, COLOR_YELLOW, "Press \uE005 to download from QR");
pp2d_draw_text_center(GFX_TOP, 140, 0.7, 0.7, COLOR_YELLOW, "Or \uE045 to quit");
pp2d_end_draw();
return;
case MODE_THEMES:
pp2d_draw_textf(7, 3, 0.6, 0.6, list->shuffle_count <= 10 ? COLOR_WHITE : COLOR_RED, "Selected: %i/10", list->shuffle_count);
break;
default:
break;
}
Splash_s current_splash = splashes_list[selected_splash];
if (preview_mode)
// Scroll the menu up or down if the selected theme is out of its bounds
//----------------------------------------------------------------
for(int i = 0; i < list->entries_count; i++) {
if(list->entries_count <= ENTRIES_PER_SCREEN) break;
if(list->scroll > list->selected_entry)
list->scroll--;
if((i < list->selected_entry) && \
((list->selected_entry - list->scroll) >= ENTRIES_PER_SCREEN) && \
(list->scroll != (i - ENTRIES_PER_SCREEN)))
list->scroll++;
}
//----------------------------------------------------------------
// Show arrows if there are themes out of bounds
//----------------------------------------------------------------
if(list->scroll > 0)
pp2d_draw_texture(TEXTURE_ARROW, 155, 6);
if(list->scroll + ENTRIES_PER_SCREEN < list->entries_count)
pp2d_draw_texture_flip(TEXTURE_ARROW, 155, 224, VERTICAL);
for(int i = list->scroll; i < (ENTRIES_PER_SCREEN + list->scroll); i++)
{
pp2d_begin_draw(GFX_TOP, GFX_LEFT);
pp2d_draw_texture_part(TEXTURE_PREVIEW, 0, 0, 0, 0, 400, 240);
pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
pp2d_draw_texture_part(TEXTURE_PREVIEW, 0, 0, 40, 240, 320, 240);
} else {
draw_base_interface();
pp2d_draw_text_center(GFX_TOP, 4, 0.5, 0.5, COLOR_WHITE, "Splash mode");
wchar_t title[0x40] = {0};
utf16_to_utf32((u32*)title, current_splash.name, 0x40);
pp2d_draw_wtext_wrap(20, 30, 0.7, 0.7, COLOR_WHITE, 380, title);
wchar_t author[0x40] = {0};
utf16_to_utf32((u32*)author, current_splash.author, 0x40);
pp2d_draw_text(20, 50, 0.5, 0.5, COLOR_WHITE, "By: ");
pp2d_draw_wtext_wrap(44, 50, 0.5, 0.5, COLOR_WHITE, 380, author);
wchar_t description[0xa6] = {0};
utf16_to_utf32((u32*)description, current_splash.desc, 0xb0);
pp2d_draw_wtext_wrap(20, 65, 0.5, 0.5, COLOR_WHITE, 363, description);
if(i >= list->entries_count) break;
pp2d_draw_wtext_center(GFX_TOP, 180, 0.7, 0.7, COLOR_WHITE, L"\uE000 Install Splash \uE004 Switch to Themes");
pp2d_draw_wtext_center(GFX_TOP, 210, 0.7, 0.7, COLOR_WHITE, L"\uE002 Delete current Splash");
pp2d_draw_wtext_center(GFX_TOP, 150, 0.7, 0.7, COLOR_WHITE, L"\uE003 Preview Splash \uE005 Scan QRCode");
pp2d_draw_wtext(130, 120, 0.6, 0.6, COLOR_WHITE, L"");
current_entry = list->entries[i];
pp2d_draw_on(GFX_BOTTOM, GFX_LEFT);
for (int i = 0; i < splash_count; i++) {
if (splash_count <= THEMES_PER_SCREEN)
break;
wchar_t name[0x41] = {0};
utf16_to_utf32((u32*)name, current_entry.name, 0x40);
if (splash_vertical_scroll > selected_splash)
splash_vertical_scroll--;
int vertical_offset = 48 * (i - list->scroll);
u32 font_color = COLOR_WHITE;
if ((i < selected_splash) && \
((selected_splash - splash_vertical_scroll) >= THEMES_PER_SCREEN) && \
(splash_vertical_scroll != ( - THEMES_PER_SCREEN)))
splash_vertical_scroll++;
}
if (splash_vertical_scroll > 0)
pp2d_draw_texture(TEXTURE_ARROW, 155, 6);
if (splash_vertical_scroll + THEMES_PER_SCREEN < splash_count)
pp2d_draw_texture_flip(TEXTURE_ARROW, 155, 224, VERTICAL);
for (int i = splash_vertical_scroll; i < (THEMES_PER_SCREEN + splash_vertical_scroll); i++)
if(i == list->selected_entry)
{
if (i >= splash_count)
break;
current_splash = splashes_list[i];
wchar_t name[0x106] = {0};
utf16_to_utf32((u32*)name, current_splash.name, 0x106);
int vertical_offset = 48 * (i-splash_vertical_scroll);
u32 font_color = COLOR_WHITE;
if (i == selected_splash)
{
font_color = COLOR_BLACK;
pp2d_draw_rectangle(0, 24 + vertical_offset, 320, 48, COLOR_CURSOR);
}
pp2d_draw_wtext(54, 40 + vertical_offset, 0.55, 0.55, font_color, name);
if (!current_splash.placeholder_color)
pp2d_draw_texture(current_splash.icon_id, 0, 24 + vertical_offset);
else
pp2d_draw_rectangle(0, 24 + vertical_offset, 48, 48, current_splash.placeholder_color);
font_color = COLOR_BLACK;
pp2d_draw_rectangle(0, 24 + vertical_offset, 320, 48, COLOR_CURSOR);
}
pp2d_draw_wtext(54, 40 + vertical_offset, 0.55, 0.55, font_color, name);
if(!current_entry.placeholder_color)
pp2d_draw_texture(current_entry.icon_id, 0, 24 + vertical_offset);
else
pp2d_draw_rectangle(0, 24 + vertical_offset, 48, 48, current_entry.placeholder_color);
if(current_entry.in_shuffle)
pp2d_draw_texture_blend(TEXTURE_SHUFFLE, 280, 32 + vertical_offset, font_color);
}
pp2d_end_draw();
}

View File

@@ -103,7 +103,7 @@ Result close_archives(void)
return 0;
}
u64 file_to_buf(FS_Path path, FS_Archive archive, char** buf)
u32 file_to_buf(FS_Path path, FS_Archive archive, char** buf)
{
Handle file;
Result res = 0;
@@ -114,7 +114,7 @@ u64 file_to_buf(FS_Path path, FS_Archive archive, char** buf)
*buf = calloc(1, size);
FSFILE_Read(file, NULL, 0, *buf, size);
FSFILE_Close(file);
return size;
return (u32)size;
}
u32 zip_file_to_buf(char *file_name, u16 *zip_path, char **buf)

197
source/loading.c Normal file
View File

@@ -0,0 +1,197 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2017 Alex Taber ("astronautlevel"), Dawid Eckert ("daedreth")
*
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include "loading.h"
#include "pp2d/pp2d/pp2d.h"
#include "fs.h"
#include "unicode.h"
u32 load_data(char * filename, Entry_s entry, char ** buf)
{
if(entry.is_zip)
{
return zip_file_to_buf(filename+1, entry.path, buf); //the first character will always be '/' because of the other case
}
else
{
u16 path[0x106] = {0};
strucat(path, entry.path);
struacat(path, filename);
return file_to_buf(fsMakePath(PATH_UTF16, path), ArchiveSD, buf);
}
}
static void parse_smdh(Entry_s * entry, const ssize_t textureID, const u16 * fallback_name)
{
pp2d_free_texture(textureID);
char *info_buffer = NULL;
u64 size = load_data("/info.smdh", *entry, &info_buffer);
if(!size)
{
free(info_buffer);
memcpy(entry->name, fallback_name, 0x80);
utf8_to_utf16(entry->desc, (u8*)"No description", 0x100);
utf8_to_utf16(entry->author, (u8*)"Unknown author", 0x80);
entry->placeholder_color = RGBA8(rand() % 255, rand() % 255, rand() % 255, 255);
return;
}
memcpy(entry->name, info_buffer + 0x08, 0x80);
memcpy(entry->desc, info_buffer + 0x88, 0x100);
memcpy(entry->author, info_buffer + 0x188, 0x80);
u16 *icon_data = malloc(0x1200);
memcpy(icon_data, info_buffer + 0x24C0, 0x1200);
free(info_buffer);
const u32 width = 48, height = 48;
u32 *image = malloc(width*height*sizeof(u32));
for(u32 x = 0; x < width; x++)
{
for(u32 y = 0; y < height; y++)
{
unsigned int dest_pixel = (x + y*width);
unsigned int source_pixel = (((y >> 3) * (width >> 3) + (x >> 3)) << 6) + ((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3));
image[dest_pixel] = RGB565_TO_ABGR8(icon_data[source_pixel]);
}
}
free(icon_data);
pp2d_load_texture_memory(textureID, (u8*)image, (u32)width, (u32)height);
free(image);
entry->icon_id = textureID;
}
static int compare_entries(const void * a, const void * b)
{
Entry_s *entry_a = (Entry_s *)a;
Entry_s *entry_b = (Entry_s *)b;
return memcmp(entry_a->name, entry_b->name, 0x40*sizeof(u16));
}
static void sort_list(Entry_List_s * list)
{
qsort(list->entries, list->entries_count, sizeof(Entry_s), compare_entries); //alphabet sort
}
Result load_entries(const char * loading_path, Entry_List_s * list)
{
Handle dir_handle;
Result res = FSUSER_OpenDirectory(&dir_handle, ArchiveSD, fsMakePath(PATH_ASCII, loading_path));
if(R_FAILED(res))
return res;
u32 entries_read = 1;
while(entries_read)
{
FS_DirectoryEntry dir_entry = {0};
res = FSDIR_Read(dir_handle, &entries_read, 1, &dir_entry);
if(R_FAILED(res) || entries_read == 0)
break;
if(!(dir_entry.attributes & FS_ATTRIBUTE_DIRECTORY) && strcmp(dir_entry.shortExt, "ZIP"))
continue;
list->entries_count++;
list->entries = realloc(list->entries, list->entries_count * sizeof(Entry_s));
if(list->entries == NULL)
break;
Entry_s * current_entry = &(list->entries[list->entries_count-1]);
memset(current_entry, 0, sizeof(Entry_s));
struacat(current_entry->path, loading_path);
strucat(current_entry->path, dir_entry.name);
current_entry->is_zip = !strcmp(dir_entry.shortExt, "ZIP");
ssize_t iconID = TEXTURE_ICON + list->entries_count;
parse_smdh(current_entry, iconID, dir_entry.name);
}
FSDIR_Close(dir_handle);
sort_list(list);
return res;
}
static u16 previous_path[0x106] = {0};
bool load_preview(Entry_List_s list, int * preview_offset)
{
if(list.entries == NULL) return false;
Entry_s entry = list.entries[list.selected_entry];
if(!memcmp(&previous_path, &entry.path, 0x106*sizeof(u16))) return true;
else memcpy(&previous_path, &entry.path, 0x106*sizeof(u16));
// free the previously loaded preview. wont do anything if there wasnt one
pp2d_free_texture(TEXTURE_PREVIEW);
char *preview_buffer = NULL;
u64 size = load_data("/preview.png", entry, &preview_buffer);
if(!size)
{
free(preview_buffer);
return false;
}
bool ret = false;
u8 * image = NULL;
unsigned int width = 0, height = 0;
if((lodepng_decode32(&image, &width, &height, (u8*)preview_buffer, size)) == 0) // no error
{
for(u32 i = 0; i < width; i++)
{
for(u32 j = 0; j < height; j++)
{
u32* pixel = (u32*)(image + (i + j*width) * 4);
*pixel = __builtin_bswap32(*pixel); //swap from RGBA to ABGR, needed for pp2d
}
}
pp2d_load_texture_memory(TEXTURE_PREVIEW, image, (u32)width, (u32)height);
*preview_offset = (width-400)/2;
ret = true;
}
free(image);
free(preview_buffer);
return ret;
}

View File

@@ -25,79 +25,89 @@
*/
#include "fs.h"
#include "loading.h"
#include "themes.h"
#include "splashes.h"
#include "draw.h"
#include "common.h"
#include "camera.h"
#include "pp2d/pp2d/pp2d.h"
#include <time.h>
static bool homebrew = false;
int __stacksize__ = 64 * 1024;
Result archive_result;
int init_services(void)
const char * main_paths[MODE_AMOUNT] = {
"/Themes/",
"/Splashes/",
};
void init_services(void)
{
consoleDebugInit(debugDevice_SVC);
cfguInit();
ptmuInit();
acInit();
httpcInit(0);
archive_result = open_archives();
homebrew = true;
if (!envIsHomebrew())
if(envIsHomebrew())
{
homebrew = false;
} else {
s64 out;
svcGetSystemInfo(&out, 0x10000, 0);
if (out)
{
homebrew = false;
}
homebrew = !out;
}
return homebrew;
}
int exit_services(void)
void exit_services(void)
{
close_archives();
cfguExit();
ptmuExit();
httpcExit();
acExit();
return 0;
}
void exit_function(void)
{
if(homebrew)
{
APT_HardwareResetAsync();
}
else
{
srvPublishToSubscriber(0x202, 0);
}
exit_screens();
exit_services();
}
void change_selected(Entry_List_s * list, int change_value)
{
list->selected_entry += change_value;
if(change_value < 0 && list->selected_entry < 0)
list->selected_entry = list->entries_count - 1;
else
list->selected_entry %= list->entries_count;
}
int main(void)
{
srand(time(NULL));
bool homebrew = init_services();
init_services();
init_screens();
themes_list = NULL;
theme_count = 0;
Result res = get_themes(&themes_list, &theme_count);
if (R_FAILED(res))
{
//don't need to worry about possible textures (icons, previews), that's freed by pp2d itself
free(themes_list);
themes_list = NULL;
}
splash_count = 0;
splashes_list = NULL;
res = get_splashes(&splashes_list, &splash_count);
if (R_FAILED(res))
{
//don't need to worry about possible textures (icons, previews), that's freed by pp2d itself
free(splashes_list);
splashes_list = NULL;
}
Entry_List_s lists[MODE_AMOUNT] = {0};
for(int i = 0; i < MODE_AMOUNT; i++)
load_entries(main_paths[i], &lists[i]);
EntryMode current_mode = MODE_THEMES;
splash_mode = false;
int selected_splash = 0;
int selected_theme = 0;
int previously_selected = 0;
shuffle_theme_count = 0;
bool preview_mode = false;
int preview_offset = 0;
bool qr_mode = false;
while(aptMainLoop())
{
@@ -105,274 +115,188 @@ int main(void)
u32 kDown = hidKeysDown();
u32 kHeld = hidKeysHeld();
if (qr_mode)
{
take_picture();
} else if (!splash_mode)
{
draw_theme_interface(themes_list, theme_count, selected_theme, preview_mode, shuffle_theme_count);
} else {
draw_splash_interface(splashes_list, splash_count, selected_splash, preview_mode);
}
Entry_List_s * current_list = &lists[current_mode];
if (kDown & KEY_START)
{
if (homebrew)
APT_HardwareResetAsync();
else {
srvPublishToSubscriber(0x202, 0);
}
}
else if (kDown & KEY_L)
{
splash_mode = !splash_mode;
}
if(qr_mode) take_picture();
else if(preview_mode) draw_preview(preview_offset);
else draw_interface(current_list, current_mode);
pp2d_end_draw();
if (R_FAILED(archive_result) && !splash_mode)
if(kDown & KEY_START) break;
if(R_FAILED(archive_result) && current_mode == MODE_THEMES)
{
throw_error("Theme extdata does not exist\nSet a default theme from the home menu", ERROR);
throw_error("Theme extdata does not exist!\nSet a default theme from the home menu.", ERROR_LEVEL_ERROR);
continue;
}
if (kDown & KEY_R)
if(!preview_mode && !qr_mode && kDown & KEY_L) //toggle between splashes and themes
{
if (preview_mode) {
continue;
} else {
u32 out;
ACU_GetWifiStatus(&out);
if (out)
{
qr_mode = !qr_mode;
if (qr_mode) init_qr();
else exit_qr();
continue;
} else {
throw_error("Please connect to Wi-Fi before scanning QR", WARNING);
continue;
}
}
current_mode++;
current_mode %= MODE_AMOUNT;
continue;
}
if (qr_mode) continue;
if (themes_list == NULL && !splash_mode)
continue;
if (splashes_list == NULL && splash_mode)
continue;
Theme_s * current_theme = &themes_list[selected_theme];
Splash_s *current_splash = &splashes_list[selected_splash];
if (kDown & KEY_Y)
else if(!preview_mode && kDown & KEY_R) //toggle QR mode
{
if (!preview_mode)
u32 out;
ACU_GetWifiStatus(&out);
if(out)
{
if (!splash_mode)
{
if (!current_theme->has_preview)
load_theme_preview(current_theme);
preview_mode = current_theme->has_preview;
} else {
load_splash_preview(current_splash);
preview_mode = true;
}
qr_mode = !qr_mode;
if(qr_mode)
init_qr();
else
exit_qr();
}
else
{
throw_error("Please connect to Wi-Fi before scanning QR", ERROR_LEVEL_WARNING);
}
continue;
}
else if(!qr_mode && kDown & KEY_Y) //toggle preview mode
{
if(!preview_mode)
{
preview_mode = load_preview(*current_list, &preview_offset);
}
else
{
preview_mode = false;
}
continue;
}
else if(qr_mode && kDown & KEY_L) //scan a QR code while in QR mode
{
CAMU_StopCapture(PORT_BOTH);
CAMU_Activate(SELECT_NONE);
qr_mode = !scan_qr(current_mode);
CAMU_Activate(SELECT_OUT1_OUT2);
CAMU_StartCapture(PORT_BOTH);
if(!qr_mode)
{
free(current_list->entries);
memset(current_list, 0, sizeof(Entry_List_s));
load_entries(main_paths[current_mode], current_list);
}
continue;
}
//don't allow anything while the preview is up
if (preview_mode)
if(qr_mode || preview_mode || current_list->entries == NULL)
continue;
int selected_entry = current_list->selected_entry;
Entry_s * current_entry = &current_list->entries[selected_entry];
// Actions
else if (kDown & KEY_X)
if(kDown & KEY_X)
{
if (splash_mode) {
draw_splash_install(UNINSTALL);
splash_delete();
} else {
draw_theme_install(BGM_INSTALL);
bgm_install(*current_theme);
switch(current_mode)
{
case MODE_THEMES:
draw_install(INSTALL_BGM);
bgm_install(*current_entry);
break;
case MODE_SPLASHES:
break;
default:
break;
}
}
else if (kDown & KEY_A)
else if(kDown & KEY_A)
{
if (splash_mode)
switch(current_mode)
{
draw_splash_install(SINGLE_INSTALL);
splash_install(*current_splash);
svcSleepThread(5e8);
} else {
draw_theme_install(SINGLE_INSTALL);
single_install(*current_theme);
case MODE_THEMES:
draw_install(INSTALL_SINGLE);
theme_install(*current_entry);
break;
case MODE_SPLASHES:
draw_install(INSTALL_SPLASH);
splash_install(*current_entry);
break;
default:
break;
}
}
else if(kDown & KEY_B)
{
switch(current_mode)
{
case MODE_THEMES:
if(current_entry->in_shuffle) current_list->shuffle_count--;
else current_list->shuffle_count++;
current_entry->in_shuffle = !current_entry->in_shuffle;
break;
case MODE_SPLASHES:
draw_install(INSTALL_SPLASH_DELETE);
splash_delete();
default:
break;
}
//these are here just so I don't forget how to implement them - HM
//if (current_theme->in_shuffle) {
// shuffle_theme_count--;
// current_theme->in_shuffle = false;
//}
//del_theme(current_theme->path);
//get_themes(&themes_list, &theme_count);
}
else if (kDown & KEY_B)
else if(kDown & KEY_SELECT)
{
if (splash_mode)
switch(current_mode)
{
} else {
if (shuffle_theme_count < 10)
{
if (current_theme->in_shuffle) shuffle_theme_count--;
else shuffle_theme_count++;
current_theme->in_shuffle = !(current_theme->in_shuffle);
} else {
if (current_theme->in_shuffle) {
shuffle_theme_count--;
current_theme->in_shuffle = false;
case MODE_THEMES:
if(current_list->shuffle_count > 0)
{
draw_install(INSTALL_SHUFFLE);
shuffle_install(current_list->entries, current_list->entries_count);
current_list->shuffle_count = 0;
}
}
}
}
else if (kDown & KEY_SELECT)
{
if (splash_mode)
{
} else {
if (shuffle_theme_count > 0)
{
draw_theme_install(SHUFFLE_INSTALL);
shuffle_install(themes_list, theme_count);
shuffle_theme_count = 0;
}
else {
throw_error("You dont have any Shuffle selected.", WARNING);
}
else
{
throw_error("You dont have any Shuffle selected.", ERROR_LEVEL_WARNING);
}
break;
default:
break;
}
}
// Movement in the UI
else if (kDown & KEY_DOWN)
else if(kDown & KEY_UP)
{
if (splash_mode)
{
selected_splash++;
if (selected_splash >= splash_count)
selected_splash = 0;
} else {
selected_theme++;
if (selected_theme >= theme_count)
selected_theme = 0;
}
change_selected(current_list, -1);
}
else if (kDown & KEY_UP)
else if(kDown & KEY_DOWN)
{
if (splash_mode)
{
selected_splash--;
if (selected_splash < 0)
selected_splash = splash_count - 1;
} else {
selected_theme--;
if (selected_theme < 0)
selected_theme = theme_count - 1;
}
change_selected(current_list, 1);
}
// Quick moving
else if (kDown & KEY_LEFT)
else if(kDown & KEY_LEFT)
{
if (splash_mode)
{
selected_splash -= 4;
if (selected_splash < 0) selected_splash = 0;
} else {
selected_theme -= 4;
if (selected_theme < 0) selected_theme = 0;
}
change_selected(current_list, -ENTRIES_PER_SCREEN);
}
else if (kDown & KEY_RIGHT)
else if(kDown & KEY_RIGHT)
{
if (splash_mode)
{
selected_splash += 4;
if (selected_splash >= splash_count) selected_splash = splash_count-1;
} else {
selected_theme += 4;
if (selected_theme >= theme_count) selected_theme = theme_count-1;
}
change_selected(current_list, ENTRIES_PER_SCREEN);
}
// Fast scroll using circle pad
else if (kHeld & KEY_CPAD_UP)
else if(kHeld & KEY_CPAD_UP)
{
svcSleepThread(100000000);
if (splash_mode)
{
selected_splash--;
if (selected_splash < 0)
selected_splash = splash_count - 1;
} else {
selected_theme--;
if (selected_theme < 0)
selected_theme = theme_count - 1;
}
change_selected(current_list, -1);
}
else if (kHeld & KEY_CPAD_DOWN)
else if(kHeld & KEY_CPAD_DOWN)
{
svcSleepThread(100000000);
if (splash_mode)
{
selected_splash++;
if (selected_splash >= splash_count)
selected_splash = 0;
} else {
selected_theme++;
if (selected_theme >= theme_count)
selected_theme = 0;
}
change_selected(current_list, 1);
}
else if (kDown & KEY_CPAD_LEFT)
else if(kDown & KEY_CPAD_LEFT)
{
svcSleepThread(100000000);
if (splash_mode)
{
selected_splash -= 4;
if (selected_splash < 0) selected_splash = 0;
} else {
selected_theme -= 4;
if (selected_theme < 0) selected_theme = 0;
}
change_selected(current_list, -ENTRIES_PER_SCREEN);
}
else if (kDown & KEY_CPAD_RIGHT)
else if(kDown & KEY_CPAD_RIGHT)
{
svcSleepThread(100000000);
if (splash_mode)
{
selected_splash += 4;
if (selected_splash >= splash_count) selected_splash = splash_count-1;
} else {
selected_theme += 4;
if (selected_theme >= theme_count) selected_theme = theme_count-1;
}
}
if (!splash_mode && selected_theme != previously_selected)
{
current_theme->has_preview = false;
previously_selected = selected_theme;
change_selected(current_list, ENTRIES_PER_SCREEN);
}
}
exit_screens();
exit_services();
exit_function();
return 0;
}

View File

@@ -26,254 +26,34 @@
#include "splashes.h"
#include "unicode.h"
#include "fs.h"
#include "themes.h"
#include "pp2d/pp2d/pp2d.h"
#include "draw.h"
void load_splash_preview(Splash_s *splash)
{
//free the previously loaded preview. wont do anything if there wasnt one
pp2d_free_texture(TEXTURE_PREVIEW);
char *preview_buffer = NULL;
u64 size = 0;
if (!(splash->is_zip))
{
u16 path_to_preview[0x106] = {0};
strucat(path_to_preview, splash->path);
struacat(path_to_preview, "/preview.png");
size = file_to_buf(fsMakePath(PATH_UTF16, path_to_preview), ArchiveSD, &preview_buffer);
} else {
size = zip_file_to_buf("preview.png", splash->path, &preview_buffer);
}
if (!size)
{
free(preview_buffer);
return;
}
u8 * image = NULL;
unsigned int width = 0, height = 0;
if ((lodepng_decode32(&image, &width, &height, (u8*)preview_buffer, size)) == 0) // no error
{
for (u32 i = 0; i < width; i++)
{
for (u32 j = 0; j < height; j++)
{
u32 p = (i + j*width) * 4;
u8 r = *(u8*)(image + p);
u8 g = *(u8*)(image + p + 1);
u8 b = *(u8*)(image + p + 2);
u8 a = *(u8*)(image + p + 3);
*(image + p) = a;
*(image + p + 1) = b;
*(image + p + 2) = g;
*(image + p + 3) = r;
}
}
pp2d_load_texture_memory(TEXTURE_PREVIEW, image, (u32)width, (u32)height);
}
free(image);
free(preview_buffer);
}
static void parse_smdh(Splash_s *splash, ssize_t textureID, u16 *splash_name)
{
char *info_buffer = NULL;
u64 size = 0;
if (!(splash->is_zip))
{
u16 path_to_smdh[0x106] = {0};
strucat(path_to_smdh, splash->path);
struacat(path_to_smdh, "/info.smdh");
size = file_to_buf(fsMakePath(PATH_UTF16, path_to_smdh), ArchiveSD, &info_buffer);
} else {
size = zip_file_to_buf("info.smdh", splash->path, &info_buffer);
}
if (!size)
{
free(info_buffer);
memset(splash->name, 0, 0x80);
memset(splash->desc, 0, 0x100);
memset(splash->author, 0, 0x80);
memcpy(splash->name, splash_name, 0x80);
utf8_to_utf16(splash->desc, (u8*)"No description", 0x100);
utf8_to_utf16(splash->author, (u8*)"Unknown author", 0x80);
splash->placeholder_color = RGBA8(rand() % 255, rand() % 255, rand() % 255, 255);
return;
}
memcpy(splash->name, info_buffer + 0x08, 0x80);
memcpy(splash->desc, info_buffer + 0x88, 0x100);
memcpy(splash->author, info_buffer + 0x188, 0x80);
u16 *icon_data = malloc(0x1200);
memcpy(icon_data, info_buffer + 0x24C0, 0x1200);
const u32 width = 48, height = 48;
u32 *image = malloc(width*height*sizeof(u32));
for (u32 x = 0; x < width; x++)
{
for (u32 y = 0; y < height; y++)
{
unsigned int dest_pixel = (x + y*width);
unsigned int source_pixel = (((y >> 3) * (width >> 3) + (x >> 3)) << 6) + ((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3));
u8 r = ((icon_data[source_pixel] >> 11) & 0b11111) << 3;
u8 g = ((icon_data[source_pixel] >> 5) & 0b111111) << 2;
u8 b = (icon_data[source_pixel] & 0b11111) << 3;
u8 a = 0xFF;
image[dest_pixel] = (r << 24) | (g << 16) | (b << 8) | a;
}
}
pp2d_load_texture_memory(textureID, (u8*)image, (u32)width, (u32)height);
splash->icon_id = textureID;
free(image);
free(icon_data);
free(info_buffer);
}
Result get_splashes(Splash_s** splashes_list, int *splash_count)
{
Result res = 0;
Handle dir_handle;
if (R_FAILED(res = FSUSER_OpenDirectory(&dir_handle, ArchiveSD, fsMakePath(PATH_ASCII, SPLASHES_PATH))))
return res;
if (*splashes_list != NULL)
{
free(*splashes_list);
*splashes_list = NULL;
*splash_count = 0;
}
u32 entries_read = 1;
while (entries_read)
{
FS_DirectoryEntry entry = {0};
if (R_FAILED(res = FSDIR_Read(dir_handle, &entries_read, 1, &entry)) || entries_read == 0)
break;
if (!(entry.attributes & FS_ATTRIBUTE_DIRECTORY) && strcmp(entry.shortExt, "ZIP"))
continue;
*splash_count += entries_read;
*splashes_list= realloc(*splashes_list, (*splash_count) * sizeof(Splash_s));
if (splashes_list == NULL)
break;
Splash_s *current_splash = &(*splashes_list)[*splash_count-1];
memset(current_splash, 0, sizeof(Splash_s));
u16 splash_path[0x106]= {0};
struacat(splash_path, SPLASHES_PATH);
strucat(splash_path, entry.name);
char pathchar[0x106] = {0};
utf16_to_utf8((u8*) pathchar, splash_path, 0x106);
memcpy(current_splash->path, splash_path, 0x106 * sizeof(u16));
current_splash->is_zip = !strcmp(entry.shortExt, "ZIP");
ssize_t iconID = TEXTURE_PREVIEW + theme_count + *splash_count;
parse_smdh(current_splash, iconID, entry.name);
}
FSDIR_Close(dir_handle);
qsort(*splashes_list, (long)*splash_count, sizeof(Splash_s), splashcmp);
return res;
}
int splashcmp(const void* a, const void* b) //essentially a memcmp alias, so that it can be used properly with qsort
{
Splash_s *splash_a = (Splash_s *)a;
Splash_s *splash_b = (Splash_s *)b;
return memcmp(splash_a, splash_b, 0x40*sizeof(u16));
}
void splash_delete()
void splash_delete(void)
{
remove("/luma/splash.bin");
remove("/luma/splashbottom.bin");
}
void splash_install(Splash_s splash_to_install)
void splash_install(Entry_s splash)
{
char *screen_buf = NULL;
u32 size = 0;
if (splash_to_install.is_zip)
{
size = zip_file_to_buf("splash.bin", splash_to_install.path, &screen_buf);
if (size)
{
remake_file("/luma/splash.bin", ArchiveSD, sizeof(screen_buf));
buf_to_file(size, "/luma/splash.bin", ArchiveSD, screen_buf);
free(screen_buf);
screen_buf = NULL;
size = 0;
}
size = zip_file_to_buf("splashbottom.bin", splash_to_install.path, &screen_buf);
if (size)
{
remake_file("/luma/splashbottom.bin", ArchiveSD, sizeof(screen_buf));
buf_to_file(size, "/luma/splashbottom.bin", ArchiveSD, screen_buf);
free(screen_buf);
screen_buf = NULL;
size = 0;
}
} else {
u16 path[0x106] = {0};
memcpy(path, splash_to_install.path, 0x106 * sizeof(u16));
struacat(path, "/splash.bin");
size = file_to_buf(fsMakePath(PATH_UTF16, path), ArchiveSD, &screen_buf);
if (size)
{
remake_file("/luma/splash.bin", ArchiveSD, sizeof(screen_buf));
buf_to_file(size, "/luma/splash.bin", ArchiveSD, screen_buf);
free(screen_buf);
screen_buf = NULL;
size = 0;
}
u32 size = load_data("/splash.bin", splash, &screen_buf);
remake_file("/luma/splash.bin", ArchiveSD, size);
buf_to_file(size, "/luma/splash.bin", ArchiveSD, screen_buf);
memcpy(path, splash_to_install.path, 0x106 * sizeof(u16));
struacat(path, "/splashbottom.bin");
size = file_to_buf(fsMakePath(PATH_UTF16, path), ArchiveSD, &screen_buf);
if (size)
{
remake_file("/luma/splashbottom.bin", ArchiveSD, sizeof(screen_buf));
buf_to_file(size, "/luma/splashbottom.bin", ArchiveSD, screen_buf);
free(screen_buf);
screen_buf = NULL;
size = 0;
}
}
size = load_data("/splashbottom.bin", splash, &screen_buf);
remake_file("/luma/splashbottom.bin", ArchiveSD, size);
buf_to_file(size, "/luma/splashbottom.bin", ArchiveSD, screen_buf);
char *config_buf;
size = file_to_buf(fsMakePath(PATH_ASCII, "/luma/config.bin"), ArchiveSD, &config_buf);
if (size)
if(size)
{
if (config_buf[0xC] == 0)
if(config_buf[0xC] == 0)
{
free(config_buf);
throw_error("WARNING: Splashes are disabled in Luma Config", WARNING);
throw_error("WARNING: Splashes are disabled in Luma Config", ERROR_LEVEL_WARNING);
}
}
}

View File

@@ -27,209 +27,25 @@
#include "themes.h"
#include "unicode.h"
#include "fs.h"
#include <time.h>
#include "draw.h"
#include "pp2d/pp2d/pp2d.h"
#include "pp2d/pp2d/lodepng.h"
#define BGM_MAX_SIZE 3371008
void load_theme_preview(Theme_s *theme)
{
//free the previously loaded preview. wont do anything if there wasnt one
pp2d_free_texture(TEXTURE_PREVIEW);
char *preview_buffer = NULL;
u64 size = 0;
if (!(theme->is_zip))
{
u16 path_to_preview[0x106] = {0};
strucat(path_to_preview, theme->path);
struacat(path_to_preview, "/preview.png");
size = file_to_buf(fsMakePath(PATH_UTF16, path_to_preview), ArchiveSD, &preview_buffer);
} else {
size = zip_file_to_buf("preview.png", theme->path, &preview_buffer);
}
if (!size)
{
free(preview_buffer);
return;
}
u8 * image = NULL;
unsigned int width = 0, height = 0;
int result = lodepng_decode32(&image, &width, &height, (u8*)preview_buffer, size);
if (result == 0) // no error
{
for (u32 i = 0; i < width; i++)
{
for (u32 j = 0; j < height; j++)
{
u32 p = (i + j*width) * 4;
u8 r = *(u8*)(image + p);
u8 g = *(u8*)(image + p + 1);
u8 b = *(u8*)(image + p + 2);
u8 a = *(u8*)(image + p + 3);
*(image + p) = a;
*(image + p + 1) = b;
*(image + p + 2) = g;
*(image + p + 3) = r;
}
}
theme->has_preview = true;
pp2d_load_texture_memory(TEXTURE_PREVIEW, image, (u32)width, (u32)height);
theme->preview_offset = (width-400)/2;
}
free(image);
free(preview_buffer);
}
static void parse_smdh(Theme_s *theme, ssize_t textureID, u16 *dir_name)
{
char *info_buffer = NULL;
u64 size = 0;
if (!(theme->is_zip))
{
u16 path_to_smdh[0x106] = {0};
strucat(path_to_smdh, theme->path);
struacat(path_to_smdh, "/info.smdh");
size = file_to_buf(fsMakePath(PATH_UTF16, path_to_smdh), ArchiveSD, &info_buffer);
} else {
size = zip_file_to_buf("info.smdh", theme->path, &info_buffer);
}
if (!size)
{
free(info_buffer);
memset(theme->name, 0, 0x80);
memset(theme->desc, 0, 0x100);
memset(theme->author, 0, 0x80);
memcpy(theme->name, dir_name, 0x80);
utf8_to_utf16(theme->desc, (u8*)"No description", 0x100);
utf8_to_utf16(theme->author, (u8*)"Unknown author", 0x80);
theme->placeholder_color = RGBA8(rand() % 255, rand() % 255, rand() % 255, 255);
return;
}
memcpy(theme->name, info_buffer + 0x08, 0x80);
memcpy(theme->desc, info_buffer + 0x88, 0x100);
memcpy(theme->author, info_buffer + 0x188, 0x80);
u16 *icon_data = malloc(0x1200);
memcpy(icon_data, info_buffer + 0x24C0, 0x1200);
const u32 width = 48, height = 48;
u32 *image = malloc(width*height*sizeof(u32));
for (u32 x = 0; x < width; x++)
{
for (u32 y = 0; y < height; y++)
{
unsigned int dest_pixel = (x + y*width);
unsigned int source_pixel = (((y >> 3) * (width >> 3) + (x >> 3)) << 6) + ((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3));
u8 r = ((icon_data[source_pixel] >> 11) & 0b11111) << 3;
u8 g = ((icon_data[source_pixel] >> 5) & 0b111111) << 2;
u8 b = (icon_data[source_pixel] & 0b11111) << 3;
u8 a = 0xFF;
image[dest_pixel] = (r << 24) | (g << 16) | (b << 8) | a;
}
}
pp2d_load_texture_memory(textureID, (u8*)image, (u32)width, (u32)height);
theme->icon_id = textureID;
free(image);
free(icon_data);
free(info_buffer);
}
Result get_themes(Theme_s **themes_list, int *theme_count)
{
shuffle_theme_count = 0;
Result res = 0;
Handle dir_handle;
res = FSUSER_OpenDirectory(&dir_handle, ArchiveSD, fsMakePath(PATH_ASCII, THEMES_PATH));
if (R_FAILED(res))
return res;
if (*themes_list != NULL) //used for QR reading and also for theme deletion
{
free(*themes_list);
*themes_list = NULL;
*theme_count = 0;
}
u32 entries_read = 1;
while (entries_read)
{
FS_DirectoryEntry entry = {0};
res = FSDIR_Read(dir_handle, &entries_read, 1, &entry);
if (R_FAILED(res) || entries_read == 0)
break;
if (!(entry.attributes & FS_ATTRIBUTE_DIRECTORY) && strcmp(entry.shortExt, "ZIP"))
continue;
*theme_count += entries_read;
*themes_list = realloc(*themes_list, (*theme_count) * sizeof(Theme_s));
if (*themes_list == NULL)
break;
Theme_s* current_theme = &(*themes_list)[*theme_count-1];
memset(current_theme, 0, sizeof(Theme_s));
u16 theme_path[0x106] = {0};
struacat(theme_path, THEMES_PATH);
strucat(theme_path, entry.name);
char pathchar[0x106] = {0};
utf16_to_utf8((u8*)pathchar, theme_path, 0x106);
memcpy(current_theme->path, theme_path, 0x106 * sizeof(u16));
current_theme->is_zip = !strcmp(entry.shortExt, "ZIP");
ssize_t iconID = TEXTURE_PREVIEW + *theme_count;
parse_smdh(current_theme, iconID, entry.name);
}
FSDIR_Close(dir_handle);
qsort(*themes_list, (long)*theme_count, sizeof(Theme_s), themecmp); //alphabet sort
return res;
}
int themecmp(const void* a, const void* b) //essentially a memcmp alias, so that it can be used properly with qsort
{
Theme_s *theme_a = (Theme_s *)a;
Theme_s *theme_b = (Theme_s *)b;
return memcmp(theme_a, theme_b, 0x40*sizeof(u16));
}
void del_theme(u16 *path)
void delete_theme(Entry_s theme)
{
Handle dir_handle;
Result res = FSUSER_OpenDirectory(&dir_handle, ArchiveSD, fsMakePath(PATH_UTF16, path));
if (R_SUCCEEDED(res))
Result res = FSUSER_OpenDirectory(&dir_handle, ArchiveSD, fsMakePath(PATH_UTF16, theme.path));
if(R_SUCCEEDED(res))
{
FSDIR_Close(dir_handle);
FSUSER_DeleteDirectoryRecursively(ArchiveSD, fsMakePath(PATH_UTF16, path));
FSUSER_DeleteDirectoryRecursively(ArchiveSD, fsMakePath(PATH_UTF16, theme.path));
} else
{
FSUSER_DeleteFile(ArchiveSD, fsMakePath(PATH_UTF16, path));
FSUSER_DeleteFile(ArchiveSD, fsMakePath(PATH_UTF16, theme.path));
}
}
Result bgm_install(Theme_s bgm_to_install)
Result bgm_install(Entry_s bgm_to_install)
{
char *savedata_buf;
char *thememanage_buf;
@@ -245,31 +61,23 @@ Result bgm_install(Theme_s bgm_to_install)
Result result = buf_to_file(savedata_size, "/SaveData.dat", ArchiveHomeExt, savedata_buf);
free(savedata_buf);
if (!R_SUCCEEDED(result)) return result;
if(R_FAILED(result)) return result;
if (bgm_to_install.is_zip) // Same as above but this time with bgm
{
music_size = zip_file_to_buf("bgm.bcstm", bgm_to_install.path, &music);
} else {
u16 path[0x106] = {0};
memcpy(path, bgm_to_install.path, 0x106 * sizeof(u16));
struacat(path, "/bgm.bcstm");
music_size = file_to_buf(fsMakePath(PATH_UTF16, path), ArchiveSD, &music);
}
music_size = load_data("/bgm.bcstm", bgm_to_install, &music);
if (music_size == 0)
if(music_size == 0)
{
music = calloc(1, 3371008);
} else if (music_size > 3371008) {
music = calloc(1, BGM_MAX_SIZE);
} else if(music_size > BGM_MAX_SIZE) {
free(music);
puts("musicrip");
return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_TOO_LARGE);
}
result = buf_to_file(music_size == 0 ? 3371008 : music_size, "/BgmCache.bin", ArchiveThemeExt, music);
result = buf_to_file(music_size == 0 ? BGM_MAX_SIZE : music_size, "/BgmCache.bin", ArchiveThemeExt, music);
free(music);
if (!R_SUCCEEDED(result)) return result;
if(R_FAILED(result)) return result;
file_to_buf(fsMakePath(PATH_ASCII, "/ThemeManage.bin"), ArchiveThemeExt, &thememanage_buf);
thememanage_buf[0x00] = 1;
@@ -296,23 +104,24 @@ Result bgm_install(Theme_s bgm_to_install)
result = buf_to_file(0x800, "/ThemeManage.bin", ArchiveThemeExt, thememanage_buf);
free(thememanage_buf);
if (!R_SUCCEEDED(result)) return result;
if(R_FAILED(result)) return result;
return 0;
}
// Install a single theme
Result single_install(Theme_s theme_to_install)
Result theme_install(Entry_s theme)
{
char *body;
char *music;
char *savedata_buf;
char *thememanage_buf;
u32 body_size;
u32 music_size;
u32 savedata_size;
char *body = NULL;
char *music = NULL;
char *savedata_buf = NULL;
char *thememanage_buf = NULL;
u32 body_size = 0;
u32 music_size = 0;
u32 savedata_size = 0;
savedata_size = file_to_buf(fsMakePath(PATH_ASCII, "/SaveData.dat"), ArchiveHomeExt, &savedata_buf);
DEBUGPOS("savedata: %p, %lx\n", savedata_buf, savedata_size);
savedata_buf[0x141b] = 0;
memset(&savedata_buf[0x13b8], 0, 8);
savedata_buf[0x13bd] = 3;
@@ -320,56 +129,38 @@ Result single_install(Theme_s theme_to_install)
Result result = buf_to_file(savedata_size, "/SaveData.dat", ArchiveHomeExt, savedata_buf);
free(savedata_buf);
if (!R_SUCCEEDED(result)) return result;
if(R_FAILED(result)) return result;
// Open body cache file. Test if theme is zipped
if (theme_to_install.is_zip)
{
body_size = zip_file_to_buf("body_LZ.bin", theme_to_install.path, &body);
}
else
{
u16 path[0x106] = {0};
memcpy(path, theme_to_install.path, 0x106 * sizeof(u16));
struacat(path, "/body_lz.bin");
body_size = file_to_buf(fsMakePath(PATH_UTF16, path), ArchiveSD, &body);
}
// Open body cache file.
body_size = load_data("/body_LZ.bin", theme, &body);
if (body_size == 0)
if(body_size == 0)
{
free(body);
puts("bodyrip");
DEBUGPOS("bodyrip");
return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_NOT_FOUND);
}
result = buf_to_file(body_size, "/BodyCache.bin", ArchiveThemeExt, body); // Write body data to file
free(body);
if (!R_SUCCEEDED(result)) return result;
if(R_FAILED(result)) return result;
if (theme_to_install.is_zip) // Same as above but this time with bgm
{
music_size = zip_file_to_buf("bgm.bcstm", theme_to_install.path, &music);
} else {
u16 path[0x106] = {0};
memcpy(path, theme_to_install.path, 0x106 * sizeof(u16));
struacat(path, "/bgm.bcstm");
music_size = file_to_buf(fsMakePath(PATH_UTF16, path), ArchiveSD, &music);
}
music_size = load_data("/bgm.bcstm", theme, &music);
if (music_size == 0)
if(music_size == 0)
{
music = calloc(1, 3371008);
} else if (music_size > 3371008) {
music = calloc(1, BGM_MAX_SIZE);
} else if(music_size > BGM_MAX_SIZE) {
free(music);
puts("musicrip");
DEBUGPOS("musicrip");
return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_TOO_LARGE);
}
result = buf_to_file(music_size == 0 ? 3371008 : music_size, "/BgmCache.bin", ArchiveThemeExt, music);
result = buf_to_file(music_size == 0 ? BGM_MAX_SIZE : music_size, "/BgmCache.bin", ArchiveThemeExt, music);
free(music);
if (!R_SUCCEEDED(result)) return result;
if(R_FAILED(result)) return result;
file_to_buf(fsMakePath(PATH_ASCII, "/ThemeManage.bin"), ArchiveThemeExt, &thememanage_buf);
thememanage_buf[0x00] = 1;
@@ -398,18 +189,18 @@ Result single_install(Theme_s theme_to_install)
result = buf_to_file(0x800, "/ThemeManage.bin", ArchiveThemeExt, thememanage_buf);
free(thememanage_buf);
if (!R_SUCCEEDED(result)) return result;
if(R_FAILED(result)) return result;
return 0;
}
Result shuffle_install(Theme_s *themes_list, int theme_count)
Result shuffle_install(Entry_s* themes_list, int themes_count)
{
u8 count = 0;
Theme_s *shuffle_themes[10] = {0};
Entry_s *shuffle_themes[10] = {0};
u32 body_sizes[10] = {0};
u32 bgm_sizes[10] = {0};
for (int i = 0; i < theme_count; i++)
for (int i = 0; i < themes_count; i++)
{
if (count > 10) return MAKERESULT(RL_USAGE, RS_INVALIDARG, RM_COMMON, RD_INVALID_SELECTION);
if (themes_list[i].in_shuffle)
@@ -435,10 +226,10 @@ Result shuffle_install(Theme_s *themes_list, int theme_count)
savedata_buf[0x13bd] = 3;
savedata_buf[0x13b8] = 0xff;
for (int i = 0; i < 10; i++)
for(int i = 0; i < 10; i++)
{
memset(&savedata_buf[0x13c0 + 0x8 * i], 0, 8); // clear any existing theme structure. 8 is the length of the theme structure, so 8 * i is the pos of the current one
if (count > i) // if we are still installing themes...
if(count > i) // if we are still installing themes...
{
savedata_buf[0x13c0 + (8 * i)] = i; // index
savedata_buf[0x13c0 + (8 * i) + 5] = 3; // persistence (?)
@@ -448,61 +239,39 @@ Result shuffle_install(Theme_s *themes_list, int theme_count)
Result result = buf_to_file(size, "/SaveData.dat", ArchiveHomeExt, savedata_buf);
free(savedata_buf);
if (!R_SUCCEEDED(result)) return result;
if(R_FAILED(result)) return result;
remake_file("/BodyCache_rd.bin", ArchiveThemeExt, 0x150000 * 10); // Enough space for 10 theme files
Handle body_cache_handle;
FSUSER_OpenFile(&body_cache_handle, ArchiveThemeExt, fsMakePath(PATH_ASCII, "/BodyCache_rd.bin"), FS_OPEN_WRITE, 0);
for (int i = 0; i < 10; i++)
for(int i = 0; i < count; i++)
{
if (count > i)
{
if (shuffle_themes[i]->is_zip)
{
char *body_buf;
u32 body_size = zip_file_to_buf("body_LZ.bin", shuffle_themes[i]->path, &body_buf);
body_sizes[i] = body_size;
FSFILE_Write(body_cache_handle, NULL, 0x150000 * i, body_buf, body_size, FS_WRITE_FLUSH);
free(body_buf);
} else {
u16 path[0x106] = {0};
strucat(path, shuffle_themes[i]->path);
struacat(path, "/body_LZ.bin");
char *body_buf;
u32 body_size = file_to_buf(fsMakePath(PATH_UTF16, path), ArchiveSD, &body_buf);
body_sizes[i] = body_size;
FSFILE_Write(body_cache_handle, NULL, 0x150000 * i, body_buf, body_size, FS_WRITE_FLUSH);
free(body_buf);
}
}
Entry_s * current_theme = shuffle_themes[i];
char * body_buf = NULL;
u32 body_size = load_data("/body_LZ.bin", *current_theme, &body_buf);
body_sizes[i] = body_size;
FSFILE_Write(body_cache_handle, NULL, 0x150000 * i, body_buf, body_size, FS_WRITE_FLUSH);
free(body_buf);
}
FSFILE_Close(body_cache_handle);
for (int i = 0; i < 10; i++)
for(int i = 0; i < 10; i++)
{
char bgm_cache_path[17] = {0};
sprintf(bgm_cache_path, "/BgmCache_%.2i.bin", i);
remake_file(bgm_cache_path, ArchiveThemeExt, 3371008);
if (count > i)
remake_file(bgm_cache_path, ArchiveThemeExt, BGM_MAX_SIZE);
if(count > i)
{
char *music_buf;
u32 music_size;
Entry_s * current_theme = shuffle_themes[i];
char *music_buf = NULL;
u32 music_size = music_size = load_data("/bgm.bcstm", *current_theme, &music_buf);
if (shuffle_themes[i]->is_zip)
if(!music_size)
{
music_size = zip_file_to_buf("bgm.bcstm", shuffle_themes[i]->path, &music_buf);
} else {
u16 path[0x106] = {0};
strucat(path, shuffle_themes[i]->path);
struacat(path, "/bgm.bcstm");
music_size = file_to_buf(fsMakePath(PATH_UTF16, path), ArchiveSD, &music_buf);
}
if (!music_size)
{
char *empty = calloc(1, 3371008);
buf_to_file(3371008, bgm_cache_path, ArchiveThemeExt, empty);
char *empty = calloc(1, BGM_MAX_SIZE);
buf_to_file(BGM_MAX_SIZE, bgm_cache_path, ArchiveThemeExt, empty);
bgm_sizes[i] = 0;
free(empty);
continue;
@@ -511,8 +280,8 @@ Result shuffle_install(Theme_s *themes_list, int theme_count)
buf_to_file(music_size, bgm_cache_path, ArchiveThemeExt, music_buf);
free(music_buf);
} else {
char *empty = calloc(1, 3371008);
buf_to_file(3371008, bgm_cache_path, ArchiveThemeExt, empty);
char *empty = calloc(1, BGM_MAX_SIZE);
buf_to_file(BGM_MAX_SIZE, bgm_cache_path, ArchiveThemeExt, empty);
bgm_sizes[i] = 0;
free(empty);
}
@@ -538,7 +307,7 @@ Result shuffle_install(Theme_s *themes_list, int theme_count)
*bodysizeloc = (u32) 0;
*bgmsizeloc = (u32) 0;
for (int i = 0; i < 10; i++)
for(int i = 0; i < 10; i++)
{
bodysizeloc = (u32*) (&thememanage_buf[0x338 + (4 * i)]); // body size info for shuffle themes starts at 0x338 and is 4 bytes for each theme
bgmsizeloc = (u32*) (&thememanage_buf[0x360 + (4 * i)]); // same thing for bgm but starting at 0x360
@@ -549,7 +318,7 @@ Result shuffle_install(Theme_s *themes_list, int theme_count)
result = buf_to_file(0x800, "/ThemeManage.bin", ArchiveThemeExt, thememanage_buf);
free(thememanage_buf);
if (!R_SUCCEEDED(result)) return result;
if(R_FAILED(result)) return result;
return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_COMMON, RD_SUCCESS);
}