Initial badges work
Additional cleanup probably necessary, some more feature work still in progress (zip files)
This commit is contained in:
427
source/badges.c
Normal file
427
source/badges.c
Normal 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;
|
||||
}
|
||||
@@ -3,6 +3,135 @@
|
||||
|
||||
#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)
|
||||
{
|
||||
uint32_t * buf = (uint32_t*)*bufp;
|
||||
|
||||
@@ -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_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_BADGES], staticBuf, language.draw.install_badges);
|
||||
|
||||
for(int i = 0; i < TEXT_AMOUNT; i++)
|
||||
C2D_TextOptimize(&text[i]);
|
||||
|
||||
48
source/fs.c
48
source/fs.c
@@ -37,6 +37,34 @@
|
||||
FS_Archive ArchiveSD;
|
||||
FS_Archive ArchiveHomeExt;
|
||||
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)
|
||||
{
|
||||
@@ -49,6 +77,7 @@ Result open_archives(void)
|
||||
|
||||
FS_Path home;
|
||||
FS_Path theme;
|
||||
FS_Path badge;
|
||||
|
||||
CFGU_SecureInfoGetRegion(®ionCode);
|
||||
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, "/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/" APP_TITLE), 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;
|
||||
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;
|
||||
if(R_FAILED(res = FSUSER_OpenFile(&test_handle, ArchiveThemeExt, fsMakePath(PATH_ASCII, "/ThemeManage.bin"), FS_OPEN_READ, 0))) return res;
|
||||
FSFILE_Close(test_handle);
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "music.h"
|
||||
#include "remote.h"
|
||||
#include "ui_strings.h"
|
||||
#include "badges.h"
|
||||
#include <time.h>
|
||||
|
||||
bool quit = false;
|
||||
@@ -828,6 +829,11 @@ int main(void)
|
||||
draw_mode = DRAW_MODE_LIST;
|
||||
extra_index = 1;
|
||||
}
|
||||
else if (kDown & KEY_DRIGHT)
|
||||
{
|
||||
draw_install(INSTALL_BADGES);
|
||||
install_badges();
|
||||
}
|
||||
else if (kDown & KEY_R)
|
||||
{
|
||||
extra_index = 2;
|
||||
|
||||
@@ -129,7 +129,7 @@ const Language_s language_english = {
|
||||
},
|
||||
{
|
||||
"\uE07B Browse ThemePlaza",
|
||||
NULL
|
||||
"\uE07C Install Badges"
|
||||
},
|
||||
{
|
||||
"\uE004 Sorting menu",
|
||||
@@ -210,6 +210,7 @@ const Language_s language_english = {
|
||||
.download_bgm = "Downloading BGM, please wait...",
|
||||
.dump_single = "Dumping theme, please wait...",
|
||||
.dump_all_official = "Dumping official themes, please wait...",
|
||||
.install_badges = "Installing badges, please wait...",
|
||||
.shuffle = "Shuffle: %i/10",
|
||||
},
|
||||
.fs =
|
||||
@@ -356,6 +357,10 @@ const Language_s language_english = {
|
||||
.name_folder = "Name of output folder",
|
||||
.cancel = "Cancel",
|
||||
.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...",
|
||||
.dump_single = "Extraction du thème installé,\nveuillez patienter...",
|
||||
.dump_all_official = "Extraction des thèmes officiels,\nveuillez patienter...",
|
||||
.install_badges = "Installing badges, please wait...",
|
||||
.shuffle = "Aléatoire: %i/10",
|
||||
},
|
||||
.fs =
|
||||
@@ -688,6 +694,10 @@ const Language_s language_french = {
|
||||
.name_folder = "Nom du dossier de destination",
|
||||
.cancel = "Annuler",
|
||||
.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...",
|
||||
.dump_single = "Exportando tema, aguarde...",
|
||||
.dump_all_official = "Exportando temas oficiais, aguarde...",
|
||||
.install_badges = "Installing badges, please wait...",
|
||||
.shuffle = "Shuffle: %i/10",
|
||||
},
|
||||
.fs =
|
||||
@@ -934,7 +945,7 @@ const Language_s language_portuguese = {
|
||||
.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.",
|
||||
.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.",
|
||||
.http503 = "HTTP 503 Service Unavailable\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",
|
||||
.cancel = "Cancelar",
|
||||
.done = "Pronto"
|
||||
},
|
||||
.badges =
|
||||
{
|
||||
.extdata_locked = "Ext Data Locked\nTry restarting Anemone3DS, or using\nthe CIA version instead."
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user