Initial badges work

Additional cleanup probably necessary, some more feature work still in progress (zip files)
This commit is contained in:
2024-05-30 15:45:03 -04:00
parent 8c42a23eeb
commit 4d9cde01c4
11 changed files with 681 additions and 2 deletions

42
include/badges.h Normal file
View File

@@ -0,0 +1,42 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2024 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
* 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 BADGES_H
#define BADGES_H
#include "common.h"
#include "fs.h"
#include "conversion.h"
#include "unicode.h"
#define MAX_BADGE 1000
#define BADGE_DATA_SIZE 0xF4DF80
#define BADGE_MNG_SIZE 0xD4A8
Result install_badges(void);
Result dump_badge_extdata(void);
#endif

View File

@@ -5,5 +5,6 @@
size_t bin_to_abgr(char ** bufp, size_t size); size_t bin_to_abgr(char ** bufp, size_t size);
size_t png_to_abgr(char ** bufp, size_t size, u32 *height); size_t png_to_abgr(char ** bufp, size_t size, u32 *height);
int pngToRGB565(char *png_buf, u64 fileSize, u16 *rgb_buf_64x64, u8 *alpha_buf_64x64, u16 *rgb_buf_32x32, u8 *alpha_buf_32x32, bool set_icon);
#endif #endif

View File

@@ -57,6 +57,7 @@ typedef enum InstallType_e {
INSTALL_DUMPING_THEME, INSTALL_DUMPING_THEME,
INSTALL_DUMPING_ALL_THEMES, INSTALL_DUMPING_ALL_THEMES,
INSTALL_BADGES,
INSTALL_NONE, INSTALL_NONE,
} InstallType; } InstallType;
@@ -86,6 +87,7 @@ typedef enum {
TEXT_INSTALL_DUMPING_THEME, TEXT_INSTALL_DUMPING_THEME,
TEXT_INSTALL_DUMPING_ALL_THEMES, TEXT_INSTALL_DUMPING_ALL_THEMES,
TEXT_INSTALL_BADGES,
// Other text // Other text
TEXT_VERSION, TEXT_VERSION,

View File

@@ -28,12 +28,14 @@
#define FS_H #define FS_H
#include "common.h" #include "common.h"
#include "badges.h"
#define ILLEGAL_CHARS "><\"?;:/\\+,.|[=]*" #define ILLEGAL_CHARS "><\"?;:/\\+,.|[=]*"
extern FS_Archive ArchiveSD; extern FS_Archive ArchiveSD;
extern FS_Archive ArchiveHomeExt; extern FS_Archive ArchiveHomeExt;
extern FS_Archive ArchiveThemeExt; extern FS_Archive ArchiveThemeExt;
extern FS_Archive ArchiveBadgeExt;
typedef struct { typedef struct {
u32 enable : 1; u32 enable : 1;

View File

@@ -79,6 +79,7 @@ typedef struct {
const char *download_bgm; const char *download_bgm;
const char *dump_single; const char *dump_single;
const char *dump_all_official; const char *dump_all_official;
const char *install_badges;
float start_pos; float start_pos;
const char *shuffle; const char *shuffle;
} Draw_Strings_s; } Draw_Strings_s;
@@ -161,6 +162,10 @@ typedef struct {
const char *done; const char *done;
} Themes_Strings_s; } Themes_Strings_s;
typedef struct {
const char *extdata_locked;
} Badge_Strings_s;
typedef struct { typedef struct {
Instructions_s normal_instructions[MODE_AMOUNT]; Instructions_s normal_instructions[MODE_AMOUNT];
Instructions_s install_instructions; Instructions_s install_instructions;
@@ -175,6 +180,7 @@ typedef struct {
Instructions_s remote_extra_instructions; Instructions_s remote_extra_instructions;
Splashes_Strings_s splashes; Splashes_Strings_s splashes;
Themes_Strings_s themes; Themes_Strings_s themes;
Badge_Strings_s badges;
} Language_s; } Language_s;
typedef enum { typedef enum {

427
source/badges.c Normal file
View File

@@ -0,0 +1,427 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2024 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
* 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 "badges.h"
#include "draw.h"
#include "ui_strings.h"
static Handle actHandle;
char *badgeDataBuffer;
char *badgeMngBuffer;
u16 *rgb_buf_64x64;
u16 *rgb_buf_32x32;
u8 *alpha_buf_64x64;
u8 *alpha_buf_32x32;
Result actInit(void)
{
return srvGetServiceHandle(&actHandle, "act:u");
}
Result actExit(void)
{
return svcCloseHandle(actHandle);
}
Result ACTU_Initialize(u32 sdkVersion, u32 memSize, Handle handle)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = 0x00010084;
cmdbuf[1] = sdkVersion;
cmdbuf[2] = memSize;
cmdbuf[3] = 0x20;
cmdbuf[4] = 0x0;
cmdbuf[5] = 0x0;
cmdbuf[6] = handle;
if ((ret = svcSendSyncRequest(actHandle)) != 0) return ret;
return (Result) cmdbuf[1];
}
Result ACTU_GetAccountDataBlock(u32 slot, u32 size, u32 blockId, u32 *output)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = 0x000600C2;
cmdbuf[1] = slot;
cmdbuf[2] = size;
cmdbuf[3] = blockId;
cmdbuf[4] = (size << 4) | 12;
cmdbuf[5] = (u32) output;
if ((ret = svcSendSyncRequest(actHandle)) != 0) return ret;
return (Result) cmdbuf[1];
}
void remove_exten(u16 *filename)
{
for (int i = 0; i < strulen(filename, 0x8A); ++i)
{
if (filename[i] == '.')
{
filename[i] = 0x00;
return;
}
}
}
u64 getShortcut(char *filename)
{
u64 shortcut = 0xFFFFFFFFFFFFFFFF;
char *p1 = strchr(filename, '.');
if (p1 == NULL) return shortcut;
++p1;
char *p2 = strchr(p1, '.');
if (p2 == NULL) return shortcut;
if (p2-p1 != 8) return shortcut;
unsigned int lowpath;
if (sscanf(p1, "%08x", &lowpath) != 1) return shortcut;
shortcut = 0x0004001000000000 + lowpath;
DEBUG("Shortcut %llu found for %s\n", shortcut, filename);
return shortcut;
}
int install_badge_png(FS_Path badge_path, FS_DirectoryEntry badge_file, int *badge_count, int set_id)
{
Result res;
char *file_buf = NULL;
res = file_to_buf(badge_path, ArchiveSD, &file_buf);
if (res != badge_file.fileSize)
{
return -1;
}
int badges_in_image = pngToRGB565(file_buf, badge_file.fileSize, rgb_buf_64x64, alpha_buf_64x64, rgb_buf_32x32, alpha_buf_32x32, false);
char utf8_name[512] = {0};
utf16_to_utf8((u8 *) utf8_name, badge_file.name, 0x8A);
u64 shortcut = getShortcut(utf8_name);
for (int badge = 0; badge < badges_in_image; ++badge)
{
remove_exten(badge_file.name);
for (int j = 0; j < 16; ++j) // Copy name for all 16 languages
{
memcpy(badgeDataBuffer + 0x35E80 + *badge_count * 16 * 0x8A + j * 0x8A, badge_file.name, 0x8A);
}
memcpy(badgeDataBuffer + 0x318F80 + *badge_count * 0x2800, rgb_buf_64x64 + badge * 64 * 64, 64 * 64 * 2);
memcpy(badgeDataBuffer + 0x31AF80 + *badge_count * 0x2800, alpha_buf_64x64 + badge * 64 * 64/2, 64 * 64/2);
memcpy(badgeDataBuffer + 0xCDCF80 + *badge_count * 0xA00, rgb_buf_32x32 + badge * 32 * 32, 32 * 32 * 2);
memcpy(badgeDataBuffer + 0xCDD780 + *badge_count * 0xA00, alpha_buf_32x32 + badge * 32 * 32/2, 32 * 32/2);
int badge_id = *badge_count + 1;
memcpy(badgeMngBuffer + 0x3E8 + *badge_count * 0x28 + 0x4, &badge_id, 4);
memcpy(badgeMngBuffer + 0x3E8 + *badge_count * 0x28 + 0x8, &set_id, 4);
memcpy(badgeMngBuffer + 0x3E8 + *badge_count*0x28 + 0xC, badge_count, 2);
badgeMngBuffer[0x3E8 + *badge_count*0x28 + 0x12] = 255; // Quantity Low
badgeMngBuffer[0x3E8 + *badge_count*0x28 + 0x13] = 255; // Quantity High
memcpy(badgeMngBuffer + 0x3E8 + *badge_count*0x28 + 0x18, &shortcut, 8);
memcpy(badgeMngBuffer + 0x3E8 + *badge_count*0x28 + 0x20, &shortcut, 8); // u64 shortcut[2], not sure what second is for
badgeMngBuffer[0x358 + *badge_count/8] |= 0 << (*badge_count % 8); // enabled badges bitfield
*badge_count += 1;
}
return badges_in_image;
}
int install_badge_dir(FS_DirectoryEntry set_dir, int *badge_count, int set_id)
{
Result res;
Handle folder;
int start_idx = *badge_count;
char *icon_buf = NULL;
int icon_size = 0;
FS_DirectoryEntry *badge_files = calloc(1024, sizeof(FS_DirectoryEntry));
u16 path[512] = {0};
u16 set_icon[17] = {0};
utf8_to_utf16(set_icon, (u8 *) "_seticon.png", 16);
struacat(path, "/Badges/");
strucat(path, set_dir.name);
res = FSUSER_OpenDirectory(&folder, ArchiveSD, fsMakePath(PATH_UTF16, path));
if (R_FAILED(res))
{
return -1;
}
u32 entries_read;
res = FSDIR_Read(folder, &entries_read, 1024, badge_files);
int badges_in_set = 0;
for (int i = 0; i < entries_read && *badge_count < 1000; ++i)
{
if (!strcmp(badge_files[i].shortExt, "PNG"))
{
memset(path, 0, 512 * sizeof(u16));
struacat(path, "/Badges/");
strucat(path, set_dir.name);
struacat(path, "/");
strucat(path, badge_files[i].name);
if (!memcmp(set_icon, badge_files[i].name, 16))
{
DEBUG("Found set icon\n");
icon_size = file_to_buf(fsMakePath(PATH_UTF16, path), ArchiveSD, &icon_buf);
continue;
}
badges_in_set += install_badge_png(fsMakePath(PATH_UTF16, path), badge_files[i], badge_count, set_id);
} else if (!strcmp(badge_files[i].shortExt, "ZIP"))
{
badges_in_set += 1;
// Zip install
}
}
int set_index = set_id - 1;
u32 total_count = 0xFFFF * badges_in_set;
for (int i = 0; i < 16; ++i)
{
strucat((u16 *) (badgeDataBuffer + set_index * 0x8A0 + i * 0x8A), set_dir.name);
}
badgeMngBuffer[0x3D8 + set_index/8] |= 0 << (set_index % 8);
memset(badgeMngBuffer + 0xA028 + set_index * 0x30, 0xFF, 8);
badgeMngBuffer[0xA028 + 0xC + set_index * 0x30] = 0x10; // bytes 13 and 14 are 0x2710
badgeMngBuffer[0xA028 + 0xD + set_index * 0x30] = 0x27;
memcpy(badgeMngBuffer + 0xA028 + 0x10 + set_index * 0x30, &set_id, 4);
memcpy(badgeMngBuffer + 0xA028 + 0x14 + set_index * 0x30, &set_index, 4);
memset(badgeMngBuffer + 0xA028 + 0x18 + set_index * 0x30, 0xFF, 4);
memcpy(badgeMngBuffer + 0xA028 + 0x1C + set_index * 0x30, &badges_in_set, 4);
memcpy(badgeMngBuffer + 0xA028 + 0x20 + set_index * 0x30, &total_count, 4);
memcpy(badgeMngBuffer + 0xA028 + 0x24 + set_index * 0x30, &start_idx, 4);
int icon = pngToRGB565(icon_buf, icon_size, rgb_buf_64x64, alpha_buf_64x64, rgb_buf_32x32, alpha_buf_32x32, true);
if (icon == 0)
{
DEBUG("Falling back on default icon\n");
if (icon_buf) free(icon_buf);
FILE *fp = fopen("romfs:/hb_set.png", "rb");
fseek(fp, 0L, SEEK_END);
icon_size = ftell(fp);
icon_buf = malloc(icon_size);
fseek(fp, 0L, SEEK_SET);
fread(icon_buf, 1, icon_size, fp);
fclose(fp);
pngToRGB565(icon_buf, icon_size, rgb_buf_64x64, alpha_buf_64x64, rgb_buf_32x32, alpha_buf_32x32, true);
}
free(icon_buf);
memcpy(badgeDataBuffer + 0x250F80 + set_index * 0x2000, rgb_buf_64x64, 64 * 64 * 2);
badgeMngBuffer[0x3D8 + set_index/8] |= 0 << (set_index % 8);
free(badge_files);
return badges_in_set;
}
Result install_badges(void)
{
Handle handle = 0;
Handle folder = 0;
Result res = 0;
res = actInit();
if (R_FAILED(res))
{
DEBUG("actInit() failed!\n");
return res;
}
res = ACTU_Initialize(0xB0502C8, 0, 0);
if (R_FAILED(res))
{
DEBUG("ACTU_Initialize failed! %08lx\n", res);
return res;
}
u32 nnidNum = 0xFFFFFFFF;
res = ACTU_GetAccountDataBlock(0xFE, 4, 12, &nnidNum);
if (R_FAILED(res))
{
DEBUG("ACTU_GetAccountDataBlock failed! %08lx\n", res);
return res;
}
u64 badgeDataSize = 0xF4DF80;
u64 badgeMngSize = 0xD4A8;
badgeMngBuffer = NULL;
badgeDataBuffer = NULL;
rgb_buf_64x64 = NULL;
rgb_buf_32x32 = NULL;
alpha_buf_64x64 = NULL;
alpha_buf_32x32 = NULL;
FS_DirectoryEntry *badge_files = calloc(1024, sizeof(FS_DirectoryEntry));
res = FSUSER_OpenDirectory(&folder, ArchiveSD, fsMakePath(PATH_ASCII, "/Badges/"));
if (R_FAILED(res))
{
DEBUG("Failed to open folder: %lx\n", res);
goto end;
}
u32 entries_read;
res = FSDIR_Read(folder, &entries_read, 1024, badge_files);
DEBUG("%lu files found\n", entries_read);
rgb_buf_64x64 = malloc(12*6*64*64*2); //12x6 badges in sheet max, 64x64 pixel badges, 2 bytes per RGB data
alpha_buf_64x64 = malloc(12*6*64*64/2); //Same thing, but 2 pixels of alpha data per byte
rgb_buf_32x32 = malloc(12*6*32*32*2); //Same thing, but 32x32
alpha_buf_32x32 = malloc(12*6*32*32/2);
badgeDataBuffer = calloc(1, badgeDataSize);
badgeMngBuffer = calloc(1, badgeMngSize);
int badge_count = 0;
int set_count = 0;
int default_set = 0;
int default_set_count = 0;
int default_idx = 0;
for (int i = 0; i < entries_read && badge_count < 1000; ++i)
{
if (!strcmp(badge_files[i].shortExt, "PNG"))
{
if (default_set == 0)
{
set_count += 1;
default_set = set_count;
default_idx = badge_count;
}
u16 path[0x512] = {0};
struacat(path, "/Badges/");
strucat(path, badge_files[i].name);
default_set_count += install_badge_png(fsMakePath(PATH_UTF16, path), badge_files[i], &badge_count, default_set);
} else if (!strcmp(badge_files[i].shortExt, "ZIP"))
{
if (default_set == 0)
{
set_count += 1;
default_set = set_count;
}
// Zip install
} else if (badge_files[i].attributes & FS_ATTRIBUTE_DIRECTORY)
{
set_count += 1;
install_badge_dir(badge_files[i], &badge_count, set_count);
}
}
if (default_set != 0)
{
int default_index = default_set - 1;
u32 total_count = 0xFFFF * default_set_count;
for (int i = 0; i < 16; ++i)
{
u16 name[0x8A/2] = {0};
utf8_to_utf16(name, (u8 *) "Other Badges", 0x8A);
memcpy(badgeDataBuffer + default_index * 0x8A0 + i * 0x8A, &name, 0x8A);
}
badgeMngBuffer[0x3D8 + default_index/8] |= 0 << (default_index % 8);
memset(badgeMngBuffer + 0xA028 + default_index * 0x30, 0xFF, 8);
badgeMngBuffer[0xA028 + 0xC + default_index * 0x30] = 0x10; // bytes 13 and 14 are 0x2710
badgeMngBuffer[0xA028 + 0xD + default_index * 0x30] = 0x27;
memcpy(badgeMngBuffer + 0xA028 + 0x10 + default_index * 0x30, &default_set, 4);
memcpy(badgeMngBuffer + 0xA028 + 0x14 + default_index * 0x30, &default_index, 4);
memset(badgeMngBuffer + 0xA028 + 0x18 + default_index * 0x30, 0xFF, 4);
memcpy(badgeMngBuffer + 0xA028 + 0x1C + default_index * 0x30, &default_set_count, 4);
memcpy(badgeMngBuffer + 0xA028 + 0x20 + default_index * 0x30, &total_count, 4);
memcpy(badgeMngBuffer + 0xA028 + 0x24 + default_index * 0x30, &default_idx, 4);
FILE *fp = fopen("romfs:/anemone_set.png", "rb");
fseek(fp, 0L, SEEK_END);
ssize_t size = ftell(fp);
char *icon_buf = malloc(size);
fseek(fp, 0L, SEEK_SET);
fread(icon_buf, 1, size, fp);
fclose(fp);
pngToRGB565(icon_buf, size, rgb_buf_64x64, alpha_buf_64x64, rgb_buf_32x32, alpha_buf_32x32, true);
free(icon_buf);
memcpy(badgeDataBuffer + 0x250F80 + default_index * 0x2000, rgb_buf_64x64, 64 * 64 * 2);
}
res = buf_to_file(badgeDataSize, fsMakePath(PATH_ASCII, "/BadgeData.dat"), ArchiveBadgeExt, badgeDataBuffer);
if (res)
{
DEBUG("Error writing badge data! %lx\n", res);
throw_error(language.badges.extdata_locked, ERROR_LEVEL_WARNING);
goto end;
}
u32 total_badges = 0xFFFF * badge_count; // Quantity * unique badges?
badgeMngBuffer[0x4] = set_count; // badge set count
memcpy(badgeMngBuffer + 0x8, &badge_count, 4);
badgeMngBuffer[0x10] = 0xFF; // Selected Set - 0xFFFFFFFF is all badges
badgeMngBuffer[0x11] = 0xFF;
badgeMngBuffer[0x12] = 0xFF;
badgeMngBuffer[0x13] = 0xFF;
memcpy(badgeMngBuffer + 0x18, &total_badges, 4);
memcpy(badgeMngBuffer + 0x1C, &nnidNum, 4);
for (int i = set_count; i < 100; ++i) // default values for remaining sets
{
memset(badgeMngBuffer + 0xA028 + 0x30 * i, 0xFF, 0x8);
memset(badgeMngBuffer + 0xA028 + 0x30 * i + 0x8, 0x00, 0x4);
badgeMngBuffer[0xA028 + 0x30 * i + 0xC] = 0x10;
badgeMngBuffer[0xA028 + 0x30 * i + 0xD] = 0x27;
memset(badgeMngBuffer + 0xA028 + 0x30 * i + 0xE, 0x0, 0x2);
memset(badgeMngBuffer + 0xA028 + 0x30 * i + 0x10, 0xFF, 0xC);
memset(badgeMngBuffer + 0xA028 + 0x30 * i + 0x1C, 0x00, 0x8);
memset(badgeMngBuffer + 0xA028 + 0x30 * i + 0x24, 0xFF, 0x4);
memset(badgeMngBuffer + 0xA028 + 0x30 * i + 0x28, 0x00, 0x8);
}
res = FSUSER_OpenFile(&handle, ArchiveBadgeExt, fsMakePath(PATH_ASCII, "/BadgeMngFile.dat"), FS_OPEN_READ, 0);
if (res == 0)
{
FSFILE_Read(handle, NULL, 0xB2E8, badgeMngBuffer+0xB2E8, 360 * 0x18);
FSFILE_Close(handle);
}
res = buf_to_file(badgeMngSize, fsMakePath(PATH_ASCII, "/BadgeMngFile.dat"), ArchiveBadgeExt, badgeMngBuffer);
if (res)
{
DEBUG("Error writing badge manage data! %lx\n", res);
throw_error(language.badges.extdata_locked, ERROR_LEVEL_WARNING);
goto end;
}
end:
if (rgb_buf_64x64) free(rgb_buf_64x64);
if (alpha_buf_64x64) free(alpha_buf_64x64);
if (rgb_buf_32x32) free(rgb_buf_32x32);
if (alpha_buf_32x32) free(alpha_buf_32x32);
if (handle) FSFILE_Close(handle);
if (folder) FSDIR_Close(folder);
if (badgeDataBuffer) free(badgeDataBuffer);
if (badgeMngBuffer) free(badgeMngBuffer);
if (badge_files) free(badge_files);
return res;
}

View File

@@ -3,6 +3,135 @@
#include <png.h> #include <png.h>
int pngToRGB565(char *png_buf, u64 fileSize, u16 *rgb_buf_64x64, u8 *alpha_buf_64x64, u16 *rgb_buf_32x32, u8 *alpha_buf_32x32, bool set_icon)
{
if (png_buf == NULL) return 0;
if (fileSize < 8 || png_sig_cmp((png_bytep) png_buf, 0, 8))
{
return 0;
}
u32 *buf = (u32 *) png_buf;
FILE *fp = fmemopen(buf, fileSize, "rb");
png_bytep *row_pointers = NULL;
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop info = png_create_info_struct(png);
png_init_io(png, fp);
png_read_info(png, info);
u32 width = png_get_image_width(png, info);
u32 height = png_get_image_height(png, info);
png_byte color_type = png_get_color_type(png, info);
png_byte bit_depth = png_get_bit_depth(png, info);
if (set_icon && (width != 48 || height != 48))
{
DEBUG("Invalid set icon?\n");
return 0;
}
if (!set_icon && (width < 64 || height < 64 || width % 64 != 0 || height % 64 != 0 || width > 12 * 64 || height > 6 * 64))
{
DEBUG("Invalid png found...\n");
return 0;
}
if (bit_depth == 16)
png_set_strip_16(png);
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(png);
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
png_set_expand_gray_1_2_4_to_8(png);
if (png_get_valid(png, info, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(png);
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);
if (color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png);
png_read_update_info(png, info);
u32 x, y, r, g, b, a;
memset(alpha_buf_64x64, 0, 12*6*64*64/2);
memset(alpha_buf_32x32, 0, 12*6*32*32/2);
row_pointers = malloc(sizeof(png_bytep) * height);
for (y = 0; y < height; y++)
{
row_pointers[y] = (png_byte *) malloc(png_get_rowbytes(png, info));
}
png_read_image(png, row_pointers);
png_destroy_read_struct(&png, &info, NULL);
if (fp) fclose(fp);
for (y = 0; y < height; ++y)
{
png_bytep row = row_pointers[y];
for (x = 0; x < width; ++x)
{
png_bytep px = &(row[x * 4]);
r = px[0] >> 3;
g = px[1] >> 2;
b = px[2] >> 3;
a = px[3] >> 4;
// rgb565 conversion code adapted from GYTB
int rgb565_index = 8*64*((y/8)%8) | 64*((x/8)%8) | 32*((y/4)%2) | 16*((x/4)%2) | 8*((y/2)%2) | 4*((x/2)%2) | 2*(y%2) | (x%2);
rgb565_index |= 64*64*(height/64)*(x/64) + 64*64*(y/64); // account for multiple badges from 1 image
rgb_buf_64x64[rgb565_index] = (r << 11) | (g << 5) | b;
alpha_buf_64x64[rgb565_index / 2] |= a << (4 * (x % 2));
}
}
for (y = 0; y < height; y += 2)
{
png_bytep row = row_pointers[y];
png_bytep nextrow = row_pointers[y+1];
for (x = 0; x < width; x += 2)
{
png_bytep px1 = &(row[x * 4]);
png_bytep px2 = &(nextrow[x * 4]);
png_bytep px3 = &(row[(x + 1) * 4]);
png_bytep px4 = &(nextrow[(x + 1) * 4]);
r = (px1[0] + px2[0] + px3[0] + px4[0]) >> 5;
g = (px1[1] + px2[1] + px3[1] + px4[1]) >> 4;
b = (px1[2] + px2[2] + px3[2] + px4[2]) >> 5;
a = (px1[3] + px2[3] + px3[3] + px4[3]) >> 6;
int x2 = x/2;
int y2 = y/2;
int rgb565_index = 4*64*((y2/8)%4) | 64*((x2/8)%4) | 32*((y2/4)%2) | 16*((x2/4)%2) | 8*((y2/2)%2) | 4*((x2/2)%2) | 2*(y2%2) | (x2%2);
rgb565_index |= 32*32*(height/64)*(x/64) + 32*32*(y/64);
rgb_buf_32x32[rgb565_index] = (r << 11) | (g << 5) | b;
alpha_buf_32x32[rgb565_index / 2] |= a << (4 * (x2%2));
}
}
for (y = 0; y < height; y++)
{
free(row_pointers[y]);
}
free(row_pointers);
if (!set_icon)
return (height/64)*(width/64);
else
return (height/48)*(width/48);
}
static void rotate_agbr_counterclockwise(char ** bufp, size_t size, size_t width) static void rotate_agbr_counterclockwise(char ** bufp, size_t size, size_t width)
{ {
uint32_t * buf = (uint32_t*)*bufp; uint32_t * buf = (uint32_t*)*bufp;

View File

@@ -128,6 +128,7 @@ void init_screens(void)
C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_BGM], staticBuf, language.draw.download_bgm); C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_BGM], staticBuf, language.draw.download_bgm);
C2D_TextParse(&text[TEXT_INSTALL_DUMPING_THEME], staticBuf, language.draw.dump_single); C2D_TextParse(&text[TEXT_INSTALL_DUMPING_THEME], staticBuf, language.draw.dump_single);
C2D_TextParse(&text[TEXT_INSTALL_DUMPING_ALL_THEMES], staticBuf, language.draw.dump_all_official); C2D_TextParse(&text[TEXT_INSTALL_DUMPING_ALL_THEMES], staticBuf, language.draw.dump_all_official);
C2D_TextParse(&text[TEXT_INSTALL_BADGES], staticBuf, language.draw.install_badges);
for(int i = 0; i < TEXT_AMOUNT; i++) for(int i = 0; i < TEXT_AMOUNT; i++)
C2D_TextOptimize(&text[i]); C2D_TextOptimize(&text[i]);

View File

@@ -37,6 +37,34 @@
FS_Archive ArchiveSD; FS_Archive ArchiveSD;
FS_Archive ArchiveHomeExt; FS_Archive ArchiveHomeExt;
FS_Archive ArchiveThemeExt; FS_Archive ArchiveThemeExt;
FS_Archive ArchiveBadgeExt;
Result createExtSaveData(u32 extdataID)
{
u8 null_smdh[0x36C0] = {0};
Handle *handle = fsGetSessionHandle();
u32 *cmdbuf = getThreadCommandBuffer();
u32 directory_limit = 1000;
u32 file_limit = 1000;
cmdbuf[0] = 0x08300182;
cmdbuf[1] = MEDIATYPE_SD;
cmdbuf[2] = extdataID;
cmdbuf[3] = 0;
cmdbuf[4] = 0x36C0;
cmdbuf[5] = directory_limit;
cmdbuf[6] = file_limit;
cmdbuf[7] = (0x36C0 << 4) | 0xA;
cmdbuf[8] = (u32)&null_smdh;
Result ret = 0;
if ((ret = svcSendSyncRequest(*handle)))
return ret;
return cmdbuf[1];
}
Result open_archives(void) Result open_archives(void)
{ {
@@ -49,6 +77,7 @@ Result open_archives(void)
FS_Path home; FS_Path home;
FS_Path theme; FS_Path theme;
FS_Path badge;
CFGU_SecureInfoGetRegion(&regionCode); CFGU_SecureInfoGetRegion(&regionCode);
switch(regionCode) switch(regionCode)
@@ -78,6 +107,7 @@ Result open_archives(void)
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/Themes"), FS_ATTRIBUTE_DIRECTORY); FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/Themes"), FS_ATTRIBUTE_DIRECTORY);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/Splashes"), FS_ATTRIBUTE_DIRECTORY); FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/Splashes"), FS_ATTRIBUTE_DIRECTORY);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/Badges"), FS_ATTRIBUTE_DIRECTORY);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/3ds"), FS_ATTRIBUTE_DIRECTORY); FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/3ds"), FS_ATTRIBUTE_DIRECTORY);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/3ds/" APP_TITLE), FS_ATTRIBUTE_DIRECTORY); FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/3ds/" APP_TITLE), FS_ATTRIBUTE_DIRECTORY);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/3ds/" APP_TITLE "/cache"), FS_ATTRIBUTE_DIRECTORY); FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, "/3ds/" APP_TITLE "/cache"), FS_ATTRIBUTE_DIRECTORY);
@@ -94,6 +124,24 @@ Result open_archives(void)
theme.data = themePath; theme.data = themePath;
if(R_FAILED(res = FSUSER_OpenArchive(&ArchiveThemeExt, ARCHIVE_EXTDATA, theme))) return res; if(R_FAILED(res = FSUSER_OpenArchive(&ArchiveThemeExt, ARCHIVE_EXTDATA, theme))) return res;
u32 badgePath[3] = {MEDIATYPE_SD, 0x000014d1, 0};
badge.type = PATH_BINARY;
badge.size = 0xC;
badge.data = badgePath;
if(R_FAILED(res = FSUSER_OpenArchive(&ArchiveBadgeExt, ARCHIVE_EXTDATA, badge)))
{
if (R_SUMMARY(res) == RS_NOTFOUND)
{
createExtSaveData(0x000014d1);
FSUSER_OpenArchive(&ArchiveBadgeExt, ARCHIVE_EXTDATA, badge);
remake_file(fsMakePath(PATH_ASCII, "/BadgeMngFile.dat"), ArchiveBadgeExt, BADGE_MNG_SIZE);
remake_file(fsMakePath(PATH_ASCII, "/BadgeData.dat"), ArchiveBadgeExt, BADGE_DATA_SIZE);
} else
{
return res;
}
}
Handle test_handle; Handle test_handle;
if(R_FAILED(res = FSUSER_OpenFile(&test_handle, ArchiveThemeExt, fsMakePath(PATH_ASCII, "/ThemeManage.bin"), FS_OPEN_READ, 0))) return res; if(R_FAILED(res = FSUSER_OpenFile(&test_handle, ArchiveThemeExt, fsMakePath(PATH_ASCII, "/ThemeManage.bin"), FS_OPEN_READ, 0))) return res;
FSFILE_Close(test_handle); FSFILE_Close(test_handle);

View File

@@ -33,6 +33,7 @@
#include "music.h" #include "music.h"
#include "remote.h" #include "remote.h"
#include "ui_strings.h" #include "ui_strings.h"
#include "badges.h"
#include <time.h> #include <time.h>
bool quit = false; bool quit = false;
@@ -828,6 +829,11 @@ int main(void)
draw_mode = DRAW_MODE_LIST; draw_mode = DRAW_MODE_LIST;
extra_index = 1; extra_index = 1;
} }
else if (kDown & KEY_DRIGHT)
{
draw_install(INSTALL_BADGES);
install_badges();
}
else if (kDown & KEY_R) else if (kDown & KEY_R)
{ {
extra_index = 2; extra_index = 2;

View File

@@ -129,7 +129,7 @@ const Language_s language_english = {
}, },
{ {
"\uE07B Browse ThemePlaza", "\uE07B Browse ThemePlaza",
NULL "\uE07C Install Badges"
}, },
{ {
"\uE004 Sorting menu", "\uE004 Sorting menu",
@@ -210,6 +210,7 @@ const Language_s language_english = {
.download_bgm = "Downloading BGM, please wait...", .download_bgm = "Downloading BGM, please wait...",
.dump_single = "Dumping theme, please wait...", .dump_single = "Dumping theme, please wait...",
.dump_all_official = "Dumping official themes, please wait...", .dump_all_official = "Dumping official themes, please wait...",
.install_badges = "Installing badges, please wait...",
.shuffle = "Shuffle: %i/10", .shuffle = "Shuffle: %i/10",
}, },
.fs = .fs =
@@ -356,6 +357,10 @@ const Language_s language_english = {
.name_folder = "Name of output folder", .name_folder = "Name of output folder",
.cancel = "Cancel", .cancel = "Cancel",
.done = "Done" .done = "Done"
},
.badges =
{
.extdata_locked = "Ext Data Locked\nTry restarting Anemone3DS, or using\nthe CIA version instead."
} }
}; };
@@ -542,6 +547,7 @@ const Language_s language_french = {
.download_bgm = "Téléchargement de la musique,\nveuillez patienter...", .download_bgm = "Téléchargement de la musique,\nveuillez patienter...",
.dump_single = "Extraction du thème installé,\nveuillez patienter...", .dump_single = "Extraction du thème installé,\nveuillez patienter...",
.dump_all_official = "Extraction des thèmes officiels,\nveuillez patienter...", .dump_all_official = "Extraction des thèmes officiels,\nveuillez patienter...",
.install_badges = "Installing badges, please wait...",
.shuffle = "Aléatoire: %i/10", .shuffle = "Aléatoire: %i/10",
}, },
.fs = .fs =
@@ -688,6 +694,10 @@ const Language_s language_french = {
.name_folder = "Nom du dossier de destination", .name_folder = "Nom du dossier de destination",
.cancel = "Annuler", .cancel = "Annuler",
.done = "OK" .done = "OK"
},
.badges =
{
.extdata_locked = "Ext Data Locked\nTry restarting Anemone3DS, or using\nthe CIA version instead."
} }
}; };
@@ -874,6 +884,7 @@ const Language_s language_portuguese = {
.download_bgm = "Baixando BGM, aguarde...", .download_bgm = "Baixando BGM, aguarde...",
.dump_single = "Exportando tema, aguarde...", .dump_single = "Exportando tema, aguarde...",
.dump_all_official = "Exportando temas oficiais, aguarde...", .dump_all_official = "Exportando temas oficiais, aguarde...",
.install_badges = "Installing badges, please wait...",
.shuffle = "Shuffle: %i/10", .shuffle = "Shuffle: %i/10",
}, },
.fs = .fs =
@@ -934,7 +945,7 @@ const Language_s language_portuguese = {
.http418 = "HTTP 418 I'm a teapot\nContate o administrador do site.", .http418 = "HTTP 418 I'm a teapot\nContate o administrador do site.",
.http426 = "HTTP 426 Upgrade Required\nO 3DS não consegue acessar este servidor.\nContate o administrador do site.", .http426 = "HTTP 426 Upgrade Required\nO 3DS não consegue acessar este servidor.\nContate o administrador do site.",
.http451 = "HTTP 451 Unavailable for Legal Reasons\nAlguma entidade está impedindo\no acesso ao servidor host por razões legais.", .http451 = "HTTP 451 Unavailable for Legal Reasons\nAlguma entidade está impedindo\no acesso ao servidor host por razões legais.",
.http500 = "HTTP 500 Internal Server Error\Contate o administrador do site.", .http500 = "HTTP 500 Internal Server Error\nContate o administrador do site.",
.http502 = "HTTP 502 Bad Gateway\nContate o administrador do site.", .http502 = "HTTP 502 Bad Gateway\nContate o administrador do site.",
.http503 = "HTTP 503 Service Unavailable\nContate o administrador do site.", .http503 = "HTTP 503 Service Unavailable\nContate o administrador do site.",
.http504 = "HTTP 504 Gateway Timeout\nContate o administrador do site.", .http504 = "HTTP 504 Gateway Timeout\nContate o administrador do site.",
@@ -1020,6 +1031,10 @@ const Language_s language_portuguese = {
.name_folder = "Nome da pasta de saída", .name_folder = "Nome da pasta de saída",
.cancel = "Cancelar", .cancel = "Cancelar",
.done = "Pronto" .done = "Pronto"
},
.badges =
{
.extdata_locked = "Ext Data Locked\nTry restarting Anemone3DS, or using\nthe CIA version instead."
} }
}; };