* Broke the "prepare" methods up into smaller methods * Removed the usage of linked lists and replaced them with arrays. Given that themes won't be added/removed throughout the execution of the program, we don't need to be able to dynamically add or remove elements to the list. In addition, once you got to ~50 themes, it took about .2 seconds to iterate through the entire list, which, while it may not sound like much, is a huge time sink when you're going through a list. Being able to randomly access any element in the list at the same speed is hugely beneficial. The downside to this is we need to keep track of the number of themes/splashes at all times, and, for splashes, have to do some weird type magic in order to properly iterate over it. However, these negatives are outweighed by the speed improvements.
826 lines
26 KiB
C
826 lines
26 KiB
C
#include <3ds.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "theme.h"
|
|
#include "unicode.h"
|
|
#include "minizip/unzip.h"
|
|
|
|
Result prepare_archives()
|
|
{
|
|
Result retValue;
|
|
|
|
FS_Path home;
|
|
FS_Path theme;
|
|
|
|
CFGU_SecureInfoGetRegion(®ionCode);
|
|
switch(regionCode)
|
|
{
|
|
case 1:
|
|
archive1 = 0x000002cd;
|
|
archive2 = 0x0000008f;
|
|
break;
|
|
case 2:
|
|
archive1 = 0x000002ce;
|
|
archive2 = 0x00000098;
|
|
break;
|
|
case 3:
|
|
archive1 = 0x000002cc;
|
|
archive2 = 0x00000082;
|
|
break;
|
|
default:
|
|
archive1 = 0x00;
|
|
archive2 = 0x00;
|
|
}
|
|
|
|
retValue = FSUSER_OpenArchive(&ArchiveSD, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""));
|
|
if(R_FAILED(retValue)) return retValue;
|
|
|
|
u32 homeMenuPath[3] = {MEDIATYPE_SD, archive2, 0};
|
|
home.type = PATH_BINARY;
|
|
home.size = 0xC;
|
|
home.data = homeMenuPath;
|
|
retValue = FSUSER_OpenArchive(&ArchiveHomeExt, ARCHIVE_EXTDATA, home);
|
|
if(R_FAILED(retValue)) return retValue;
|
|
|
|
u32 themePath[3] = {MEDIATYPE_SD, archive1, 0};
|
|
theme.type = PATH_BINARY;
|
|
theme.size = 0xC;
|
|
theme.data = themePath;
|
|
retValue = FSUSER_OpenArchive(&ArchiveThemeExt, ARCHIVE_EXTDATA, theme);
|
|
if(R_FAILED(retValue)) return retValue;
|
|
return 0;
|
|
}
|
|
|
|
int get_number_entries(char *path)
|
|
{
|
|
int count = 0;
|
|
Handle dir;
|
|
FSUSER_OpenDirectory(&dir, ArchiveSD, fsMakePath(PATH_ASCII, path));
|
|
while (true)
|
|
{
|
|
FS_DirectoryEntry *entry = malloc(sizeof(FS_DirectoryEntry));
|
|
u32 entries_read;
|
|
FSDIR_Read(dir, &entries_read, 1, entry);
|
|
if (entries_read)
|
|
{
|
|
if (entry->attributes == 1)
|
|
{
|
|
count++;
|
|
}
|
|
free(entry);
|
|
} else {
|
|
free(entry);
|
|
break;
|
|
}
|
|
}
|
|
FSDIR_Close(dir);
|
|
return count;
|
|
}
|
|
|
|
Result extract_current_file(unzFile zip_handle, u16 *theme_path)
|
|
{
|
|
unz_file_info *file_info = malloc(sizeof(unz_file_info)); // Make the file_info struct
|
|
char filename[64];
|
|
unzGetCurrentFileInfo(zip_handle, file_info, filename, 64, NULL, 0, NULL, 0); // Get file info, as well as filename
|
|
u32 file_size = file_info->uncompressed_size;
|
|
u16 ufilename[128] = {0};
|
|
atow(ufilename, filename); // Make the filename Unicode, as the folder name is unicode.
|
|
|
|
u16 file_path[256] = {0};
|
|
strucpy(file_path, theme_path); // Copy the base theme folder name into the file
|
|
u16 slash[2] = {0};
|
|
atow(slash, "/");
|
|
strucat(file_path, slash); // Put a / so it recognizes a directory
|
|
strucat(file_path, ufilename); // Copy the file name into the file path
|
|
|
|
Result ret;
|
|
ret = FSUSER_CreateFile(ArchiveSD, fsMakePath(PATH_UTF16, file_path), 0, file_size); // Create the file
|
|
if (R_FAILED(ret))
|
|
{
|
|
free(file_info);
|
|
return ret;
|
|
}
|
|
|
|
char *file; // Read the compressed file into a buffer
|
|
file = malloc(file_size);
|
|
unzOpenCurrentFile(zip_handle);
|
|
unzReadCurrentFile(zip_handle, file, file_size);
|
|
unzCloseCurrentFile(zip_handle);
|
|
|
|
Handle console_file; // And write it onto the SD card
|
|
ret = FSUSER_OpenFile(&console_file, ArchiveSD, fsMakePath(PATH_UTF16, file_path), FS_OPEN_WRITE, 0);
|
|
|
|
if (R_FAILED(ret))
|
|
{
|
|
free(file_info);
|
|
free(file);
|
|
return ret;
|
|
}
|
|
|
|
ret = FSFILE_Write(console_file, NULL, 0, file, file_size, FS_WRITE_FLUSH);
|
|
|
|
if (R_FAILED(ret))
|
|
{
|
|
free(file_info);
|
|
free(file);
|
|
return ret;
|
|
}
|
|
|
|
ret = FSFILE_Close(console_file);
|
|
|
|
if (R_FAILED(ret))
|
|
{
|
|
free(file_info);
|
|
free(file);
|
|
return ret;
|
|
}
|
|
|
|
free(file_info);
|
|
free(file);
|
|
|
|
return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_COMMON, RD_SUCCESS);
|
|
}
|
|
|
|
// TODO: There's a lot of duplicated code here, especially considering that we already built the paths in prepare_themes(). Maybe clean it up a bit later
|
|
Result unzip_file(char* base_path, FS_DirectoryEntry *entry, u16 *sanitized_name)
|
|
{
|
|
u16 zip_path[sizeof(entry->name) + 18] = {0}; // Make two u16*s that are big enough to hold the entire path
|
|
u16 uzipfile[sizeof(entry->name) + 18] = {0};
|
|
atow(zip_path, base_path); // Copy "/Themes/" unicode equivalent into a path
|
|
strucat(zip_path, entry->name); // And copy the unsanitized path in
|
|
atow(uzipfile, base_path);
|
|
strucat(uzipfile, sanitized_name);
|
|
u16 theme_path_u16[128] = {0};
|
|
trim_extension(theme_path_u16, zip_path); // Get rid of the extension on the unsanitized one, which later becomes the basis for the folder
|
|
|
|
FS_Path theme_path = fsMakePath(PATH_UTF16, theme_path_u16); // Turn the unsanizited name into a new directory
|
|
|
|
Result res = FSUSER_OpenDirectory(NULL, ArchiveSD, theme_path);
|
|
if (R_SUMMARY(res) == RS_NOTFOUND) // If it doesn't exist, make it
|
|
{
|
|
res = FSUSER_CreateDirectory(ArchiveSD, theme_path, FS_ATTRIBUTE_DIRECTORY);
|
|
if (R_FAILED(res)) return res;
|
|
} else if(R_FAILED(res)) return res;
|
|
|
|
char *zipfile = malloc(524); // Plop the char* equivalent into here
|
|
wtoa(zipfile, uzipfile);
|
|
|
|
unzFile zip_handle = unzOpen(zipfile); // Open up the zip file
|
|
if (zip_handle == NULL)
|
|
{
|
|
printf("Failed to open zip: %s\n\n", zipfile);
|
|
return MAKERESULT(RL_FATAL, RS_NOTFOUND, RM_COMMON, RD_NOT_FOUND);
|
|
}
|
|
|
|
unzGoToFirstFile(zip_handle); // Go to the first file and unzip it
|
|
|
|
extract_current_file(zip_handle, theme_path_u16);
|
|
while(unzGoToNextFile(zip_handle) == UNZ_OK) extract_current_file(zip_handle, theme_path_u16); // While next file exists, unzip it
|
|
unzClose(zip_handle);
|
|
res = FSUSER_DeleteFile(ArchiveSD, fsMakePath(PATH_ASCII, zipfile));
|
|
|
|
if (R_FAILED(res))
|
|
{
|
|
free(zipfile);
|
|
return res;
|
|
}
|
|
free(zipfile);
|
|
return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_COMMON, RD_SUCCESS); // And return success \o/
|
|
}
|
|
|
|
Result unzip_themes()
|
|
{
|
|
Handle themes_dir;
|
|
FSUSER_OpenDirectory(&themes_dir, ArchiveSD, fsMakePath(PATH_ASCII, "/Themes")); // Open up the Themes directory and iterate over each file
|
|
while (true)
|
|
{
|
|
FS_DirectoryEntry *entry = malloc(sizeof(FS_DirectoryEntry));
|
|
u32 entries_read;
|
|
FSDIR_Read(themes_dir, &entries_read, 1, entry);
|
|
if (entries_read) // If there is a new entry
|
|
{
|
|
if (!strcmp(entry->shortExt, "ZIP")) // if that entry is a zip
|
|
{
|
|
u16 sanitized_zip[sizeof(entry->name)] = {0};
|
|
bool changed = strip_unicode(sanitized_zip, entry->name, sizeof(entry->name)); // Here we strip out the non-ASCII characters in the zip name, as minizip doesn't like them
|
|
if (changed) // If there were any non-ascii characters
|
|
{
|
|
u16 zip_path[550] = {0};
|
|
ssize_t len = atow(zip_path, "/Themes/"); // Copy the unicode equivalent of "/Themes/" into zip_path
|
|
memcpy(&zip_path[len], entry->name, sizeof(entry->name)); // Copy the name of the zip (with unicode chars) into zip_path
|
|
u16 sanitized_zip_path[550] = {0}; // Same thing as above, this time with sanitized names
|
|
atow(sanitized_zip_path, "/Themes/");
|
|
memcpy(&sanitized_zip_path[len], sanitized_zip, sizeof(entry->name));
|
|
FSUSER_RenameFile(ArchiveSD, fsMakePath(PATH_UTF16, zip_path), ArchiveSD, fsMakePath(PATH_UTF16, sanitized_zip_path)); // Rename the zip to the sanitized one
|
|
unzip_file("/Themes/", entry, sanitized_zip); // And unzip it
|
|
} else unzip_file("/Themes/", entry, entry->name); // If it's the same, unzip it anyway
|
|
}
|
|
free(entry);
|
|
} else {
|
|
free(entry);
|
|
break;
|
|
}
|
|
}
|
|
FSDIR_Close(themes_dir);
|
|
return 0;
|
|
}
|
|
|
|
Result prepare_themes(theme_data** themes_list)
|
|
{
|
|
Handle themes_dir;
|
|
FSUSER_OpenDirectory(&themes_dir, ArchiveSD, fsMakePath(PATH_ASCII, "/Themes"));
|
|
int iter = 0;
|
|
while (true)
|
|
{
|
|
FS_DirectoryEntry *entry = malloc(sizeof(FS_DirectoryEntry));
|
|
u32 entries_read;
|
|
FSDIR_Read(themes_dir, &entries_read, 1, entry);
|
|
if (entries_read)
|
|
{
|
|
if (entry->attributes == 1)
|
|
{
|
|
theme_data *theme_info = malloc(sizeof(theme_data));
|
|
u16 theme_path[533] = {0};
|
|
atow(theme_path, "/Themes/");
|
|
strucat(theme_path, entry->name);
|
|
parseSmdh(theme_info, theme_path);
|
|
/*
|
|
FOR TESTING PURPOSES ONLY, REMOVE THIS LINE LATER!!!!
|
|
*/
|
|
theme_info->selected = true;
|
|
/*
|
|
FOR TESTING PURPOSES ONLY, REMOVE ABOVE LINE LATER!!!
|
|
*/
|
|
themes_list[iter++] = theme_info;
|
|
}
|
|
free(entry);
|
|
} else {
|
|
free(entry);
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
Result themeInstall(theme_data theme_to_install)
|
|
{
|
|
|
|
u16 *u16path = theme_to_install.path;
|
|
char *path = malloc(524);
|
|
wtoa(path, u16path);
|
|
bool music = theme_to_install.bgm;
|
|
char *body;
|
|
char *bgm;
|
|
char *saveData;
|
|
char *themeManage;
|
|
u64 bodySize;
|
|
u64 bgmSize;
|
|
u64 saveDataSize;
|
|
u64 themeManageSize;
|
|
Handle bodyHandle;
|
|
Handle bgmHandle;
|
|
Handle saveDataHandle;
|
|
Handle themeManageHandle;
|
|
|
|
Result retValue;
|
|
|
|
u32 bytes;
|
|
|
|
// Opening relevant files
|
|
char bodyPath[128];
|
|
strcpy(bodyPath, path);
|
|
strcat(bodyPath, "/body_LZ.bin");
|
|
retValue = FSUSER_OpenFile(&bodyHandle, ArchiveSD, fsMakePath(PATH_ASCII, bodyPath), FS_OPEN_READ, 0);
|
|
|
|
if(R_FAILED(retValue))
|
|
{
|
|
free(path);
|
|
return retValue;
|
|
}
|
|
|
|
retValue = FSFILE_GetSize(bodyHandle, &bodySize);
|
|
|
|
if(R_FAILED(retValue))
|
|
{
|
|
free(path);
|
|
return retValue;
|
|
}
|
|
|
|
body = malloc(bodySize);
|
|
retValue = FSFILE_Read(bodyHandle, &bytes, 0, body, (u64)bodySize);
|
|
|
|
if(R_FAILED(retValue))
|
|
{
|
|
free(path);
|
|
free(body);
|
|
return retValue;
|
|
}
|
|
|
|
FSFILE_Close(bodyHandle);
|
|
|
|
char bgmPath[128];
|
|
strcpy(bgmPath, path);
|
|
strcat(bgmPath, "/bgm.bcstm");
|
|
retValue = FSUSER_OpenFile(&bgmHandle, ArchiveSD, fsMakePath(PATH_ASCII, bgmPath), FS_OPEN_READ, 0);
|
|
|
|
if(R_FAILED(retValue))
|
|
{
|
|
free(path);
|
|
free(body);
|
|
return retValue;
|
|
}
|
|
|
|
retValue = FSFILE_GetSize(bgmHandle, &bgmSize);
|
|
|
|
if(R_FAILED(retValue))
|
|
{
|
|
free(path);
|
|
free(body);
|
|
return retValue;
|
|
}
|
|
|
|
bgm = malloc(bgmSize);
|
|
retValue = FSFILE_Read(bgmHandle, &bytes, 0, bgm, (u64)bgmSize);
|
|
|
|
if(R_FAILED(retValue))
|
|
{
|
|
free(path);
|
|
free(body);
|
|
free(bgm);
|
|
return retValue;
|
|
}
|
|
|
|
FSFILE_Close(bgmHandle);
|
|
|
|
retValue = FSUSER_OpenFile(&saveDataHandle, ArchiveHomeExt, fsMakePath(PATH_ASCII, "/SaveData.dat"), FS_OPEN_READ, 0);
|
|
|
|
if(R_FAILED(retValue))
|
|
{
|
|
free(path);
|
|
free(body);
|
|
free(bgm);
|
|
return retValue;
|
|
}
|
|
|
|
retValue = FSFILE_GetSize(saveDataHandle, &saveDataSize);
|
|
|
|
if(R_FAILED(retValue))
|
|
{
|
|
free(path);
|
|
free(body);
|
|
free(bgm);
|
|
return retValue;
|
|
}
|
|
|
|
saveData = malloc(saveDataSize);
|
|
retValue = FSFILE_Read(saveDataHandle, &bytes, 0, saveData, (u32)saveDataSize);
|
|
|
|
if(R_FAILED(retValue))
|
|
{
|
|
free(path);
|
|
free(body);
|
|
free(bgm);
|
|
free(saveData);
|
|
return retValue;
|
|
}
|
|
|
|
FSFILE_Close(saveDataHandle);
|
|
|
|
retValue = FSUSER_OpenFile(&themeManageHandle, ArchiveThemeExt, fsMakePath(PATH_ASCII, "/ThemeManage.bin"), FS_OPEN_READ | FS_OPEN_WRITE, 0);
|
|
|
|
if(R_FAILED(retValue))
|
|
{
|
|
free(path);
|
|
free(body);
|
|
free(bgm);
|
|
free(saveData);
|
|
return retValue;
|
|
}
|
|
|
|
retValue = FSFILE_GetSize(themeManageHandle, &themeManageSize);
|
|
|
|
if(R_FAILED(retValue))
|
|
{
|
|
free(path);
|
|
free(body);
|
|
free(bgm);
|
|
free(saveData);
|
|
return retValue;
|
|
}
|
|
|
|
themeManage = malloc(themeManageSize);
|
|
retValue = FSFILE_Read(themeManageHandle, &bytes, 0, themeManage, (u32)themeManageSize);
|
|
|
|
if(R_FAILED(retValue))
|
|
{
|
|
free(path);
|
|
free(body);
|
|
free(bgm);
|
|
free(saveData);
|
|
return retValue;
|
|
}
|
|
|
|
FSFILE_Close(themeManageHandle);
|
|
|
|
//Changing data in the buffers
|
|
memset(&saveData[0x13b8], 0, 8);
|
|
|
|
saveData[0x141b] = 0x00;
|
|
saveData[0x13b8] = 0xFF;
|
|
saveData[0x13b9] = 0x00;
|
|
saveData[0x13ba] = 0x00;
|
|
saveData[0x13bb] = 0x00;
|
|
saveData[0x13bc] = 0x00;
|
|
saveData[0x13bd] = 0x03;
|
|
saveData[0x13be] = 0x00;
|
|
saveData[0x13bf] = 0x00;
|
|
|
|
|
|
themeManage[0x00] = 1;
|
|
themeManage[0x01] = 0;
|
|
themeManage[0x02] = 0;
|
|
themeManage[0x03] = 0;
|
|
themeManage[0x04] = 0;
|
|
themeManage[0x05] = 0;
|
|
themeManage[0x06] = 0;
|
|
themeManage[0x07] = 0;
|
|
|
|
u32* bodySizeLocation = (u32*)(&themeManage[0x8]);
|
|
u32* bgmSizeLocation = (u32*)(&themeManage[0xC]);
|
|
*bodySizeLocation = (u32)bodySize;
|
|
*bgmSizeLocation = (u32)bgmSize;
|
|
|
|
themeManage[0x10] = 0xFF;
|
|
themeManage[0x14] = 0x01;
|
|
themeManage[0x18] = 0xFF;
|
|
themeManage[0x1D] = 0x02;
|
|
|
|
memset(&themeManage[0x338], 0, 4);
|
|
memset(&themeManage[0x340], 0, 4);
|
|
memset(&themeManage[0x360], 0, 4);
|
|
memset(&themeManage[0x368], 0, 4);
|
|
|
|
|
|
// Writing to extdata
|
|
retValue = FSUSER_OpenFile(&bodyHandle, ArchiveThemeExt, fsMakePath(PATH_ASCII, "/BodyCache.bin"), FS_OPEN_READ | FS_OPEN_WRITE, 0);
|
|
|
|
if(R_FAILED(retValue))
|
|
{
|
|
free(path);
|
|
free(body);
|
|
free(bgm);
|
|
free(saveData);
|
|
return retValue;
|
|
}
|
|
|
|
retValue = FSFILE_Write(bodyHandle, &bytes, 0, body, (u64)bodySize, FS_WRITE_FLUSH);
|
|
|
|
if(R_FAILED(retValue))
|
|
{
|
|
free(path);
|
|
free(body);
|
|
free(bgm);
|
|
free(saveData);
|
|
return retValue;
|
|
}
|
|
|
|
FSFILE_Close(bodyHandle);
|
|
free(body);
|
|
|
|
retValue = FSUSER_OpenFile(&bgmHandle, ArchiveThemeExt, fsMakePath(PATH_ASCII, "/BgmCache.bin"), FS_OPEN_READ | FS_OPEN_WRITE, 0);
|
|
|
|
if(R_FAILED(retValue))
|
|
{
|
|
free(path);
|
|
free(body);
|
|
free(bgm);
|
|
free(saveData);
|
|
return retValue;
|
|
}
|
|
|
|
if(!music) memset(bgm, 0x00, 337000 );
|
|
retValue = FSFILE_Write(bgmHandle, &bytes, 0, bgm, (u64)bgmSize, FS_WRITE_FLUSH);
|
|
|
|
if(R_FAILED(retValue))
|
|
{
|
|
free(path);
|
|
free(body);
|
|
free(bgm);
|
|
free(saveData);
|
|
return retValue;
|
|
}
|
|
|
|
FSFILE_Close(bgmHandle);
|
|
free(bgm);
|
|
|
|
retValue = FSUSER_OpenFile(&themeManageHandle, ArchiveThemeExt, fsMakePath(PATH_ASCII, "/ThemeManage.bin"), FS_OPEN_READ | FS_OPEN_WRITE, 0);
|
|
|
|
if(R_FAILED(retValue))
|
|
{
|
|
free(path);
|
|
free(body);
|
|
free(bgm);
|
|
free(saveData);
|
|
return retValue;
|
|
}
|
|
|
|
retValue = FSFILE_Write(themeManageHandle, &bytes, 0, themeManage, (u64)themeManageSize, FS_WRITE_FLUSH);
|
|
|
|
if(R_FAILED(retValue))
|
|
{
|
|
free(path);
|
|
free(body);
|
|
free(bgm);
|
|
free(saveData);
|
|
return retValue;
|
|
}
|
|
|
|
FSFILE_Close(themeManageHandle);
|
|
free(themeManage);
|
|
|
|
retValue = FSUSER_OpenFile(&saveDataHandle, ArchiveHomeExt, fsMakePath(PATH_ASCII, "/SaveData.dat"), FS_OPEN_READ | FS_OPEN_WRITE, 0);
|
|
|
|
if(R_FAILED(retValue))
|
|
{
|
|
free(path);
|
|
free(body);
|
|
free(bgm);
|
|
free(saveData);
|
|
return retValue;
|
|
}
|
|
|
|
retValue = FSFILE_Write(saveDataHandle, &bytes, 0, saveData, (u64)saveDataSize, FS_WRITE_FLUSH);
|
|
|
|
if(R_FAILED(retValue))
|
|
{
|
|
free(path);
|
|
free(body);
|
|
free(bgm);
|
|
free(saveData);
|
|
return retValue;
|
|
}
|
|
|
|
FSFILE_Close(saveDataHandle);
|
|
free(path);
|
|
free(body);
|
|
free(bgm);
|
|
free(saveData);
|
|
|
|
return 0;
|
|
}
|
|
|
|
Result shuffle_install(theme_data **themes, int len)
|
|
{
|
|
u8 count = 0;
|
|
theme_data *themes_to_be_shuffled[10] = {0};
|
|
u32 body_sizes[10] = {0};
|
|
u32 bgm_sizes[10] = {0};
|
|
|
|
// Load themes that are selected for shuffle
|
|
for (int iter = 0; iter < len; iter++)
|
|
{
|
|
if (themes[iter]->selected == true)
|
|
count++;
|
|
if (count > 10) return(MAKERESULT(RL_USAGE, RS_INVALIDARG, RM_COMMON, RD_INVALID_SELECTION));
|
|
themes_to_be_shuffled[iter] = themes[iter];
|
|
}
|
|
|
|
// Load and edit SaveData
|
|
u64 save_data_size;
|
|
Handle save_data_handle;
|
|
Result res = FSUSER_OpenFile(&save_data_handle, ArchiveHomeExt, fsMakePath(PATH_ASCII, "/SaveData.dat"), FS_OPEN_READ | FS_OPEN_WRITE, 0);
|
|
if (R_FAILED(res)) return res;
|
|
FSFILE_GetSize(save_data_handle, &save_data_size);
|
|
char *save_data_buffer = malloc(save_data_size);
|
|
res = FSFILE_Read(save_data_handle, NULL, 0, save_data_buffer, save_data_size);
|
|
|
|
if (R_FAILED(res))
|
|
{
|
|
free(save_data_buffer);
|
|
return res;
|
|
}
|
|
|
|
save_data_buffer[0x141b] = 1; // shuffle flag
|
|
memset(&save_data_buffer[0x13b8], 0, 8); // clear non-shuffle theme
|
|
save_data_buffer[0x13bd] = 3; // 3 flag makes it persistent (?)
|
|
save_data_buffer[0x13b8] = 0xff;
|
|
|
|
for (int i = 0; i < 10; i++) // We have to set all 10 even if we're installing less than 10 because we may need to clear previous shuffles
|
|
{
|
|
memset(&save_data_buffer[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...
|
|
{
|
|
save_data_buffer[0x13c0 + (8 * i)] = i; // index
|
|
save_data_buffer[0x13c0 + (8 * i) + 5] = 3; // persistence (?)
|
|
}
|
|
}
|
|
|
|
res = FSFILE_Write(save_data_handle, NULL, 0, save_data_buffer, save_data_size, FS_WRITE_FLUSH);
|
|
free(save_data_buffer);
|
|
if (R_FAILED(res)) return res;
|
|
FSFILE_Close(save_data_handle);
|
|
|
|
// Writing themes bodies to extdata
|
|
Handle body_cache_handle;
|
|
res = FSUSER_OpenFile(&body_cache_handle, ArchiveThemeExt, fsMakePath(PATH_ASCII, "/BodyCache_rd.bin"), FS_OPEN_WRITE, 0); // 3DS uses _rd for shuffle themes
|
|
if (R_FAILED(res)) return res;
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
if (count > i) // if we still have themes to install...
|
|
{
|
|
Handle body_lz; // This kind of code is repeated a ton, there has to be a better way. Either way; loading body_lz.bin into file
|
|
u16 body_path[524] = {0};
|
|
u64 body_size;
|
|
strucpy(body_path, themes_to_be_shuffled[i]->path);
|
|
struacat(body_path, "/body_LZ.bin");
|
|
res = FSUSER_OpenFile(&body_lz, ArchiveSD, fsMakePath(PATH_UTF16, body_path), FS_OPEN_READ, 0);
|
|
if (R_FAILED(res)) return res;
|
|
FSFILE_GetSize(body_lz, &body_size);
|
|
char *body_data = malloc(body_size);
|
|
res = FSFILE_Read(body_lz, NULL, 0, body_data, body_size);
|
|
if (R_FAILED(res))
|
|
{
|
|
free(body_data);
|
|
return res;
|
|
}
|
|
FSFILE_Close(body_lz);
|
|
body_sizes[i] = body_size; // We need to keep track of each theme's body size for when we write to ThemeManage.bin
|
|
res = FSFILE_Write(body_cache_handle, NULL, 0x150000 * i, body_data, body_size, FS_WRITE_FLUSH); // Each theme has 0x150000 bytes allocated for it and no less
|
|
free(body_data);
|
|
if (R_FAILED(res)) return res;
|
|
} else { // All themes have been written, write blank data to overwrite previous shuffles
|
|
char *empty = calloc(1, 0x150000);
|
|
FSFILE_Write(body_cache_handle, NULL, 0x150000 * i, empty, 0x150000, FS_WRITE_FLUSH);
|
|
free(empty);
|
|
}
|
|
}
|
|
|
|
FSFILE_Close(body_cache_handle);
|
|
|
|
// Unlike body data, the 3DS uses a separate file for each piece of BGM data. Don't ask me why.
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
Handle bgm_cache_handle;
|
|
char bgm_cache_path[17] = {0}; // BGM Path is "/BgmCache_0i.bin" (16 chars) where i is the index number, + 1 for proper null termination
|
|
sprintf(bgm_cache_path, "/BgmCache_0%i.bin", i);
|
|
res = FSUSER_OpenFile(&bgm_cache_handle, ArchiveThemeExt, fsMakePath(PATH_ASCII, bgm_cache_path), FS_OPEN_WRITE, 0);
|
|
if (R_FAILED(res)) return res;
|
|
|
|
if (count > i) // We write the bgm data if we still have themes to install and the current theme has bgm data, otherwise we write blank
|
|
{
|
|
Handle bgm_handle;
|
|
u16 bgm_path[524] = {0};
|
|
u64 bgm_size;
|
|
strucpy(bgm_path, themes_to_be_shuffled[i]->path);
|
|
struacat(bgm_path, "/bgm.bcstm");
|
|
res = FSUSER_OpenFile(&bgm_handle, ArchiveSD, fsMakePath(PATH_UTF16, bgm_path), FS_OPEN_READ, 0);
|
|
|
|
if (R_SUMMARY(res) == RS_NOTFOUND) // Despite the flag saying "install with bgm" there's no bgm for this theme -_-
|
|
{
|
|
char *empty = calloc(1, 3371008);
|
|
res = FSFILE_Write(bgm_cache_handle, NULL, 0, empty, 3371008, FS_WRITE_FLUSH);
|
|
free(empty);
|
|
bgm_sizes[i] = 0;
|
|
if (R_FAILED(res)) return res;
|
|
continue; // No need to do anything else
|
|
}
|
|
if (R_FAILED(res)) return res;
|
|
|
|
FSFILE_GetSize(bgm_handle, &bgm_size); // Copy bgm data into buffer and write out to the bgm cache
|
|
printf("%llu\n", bgm_size);
|
|
if (bgm_size > 3371008)
|
|
{
|
|
FSFILE_Close(bgm_handle);
|
|
return MAKERESULT(RL_FATAL, RS_INVALIDARG, RM_COMMON, RD_TOO_LARGE);
|
|
}
|
|
|
|
char *bgm_data = malloc(bgm_size);
|
|
Result res = FSFILE_Read(bgm_handle, NULL, 0, bgm_data, bgm_size);
|
|
if (R_FAILED(res))
|
|
{
|
|
free(bgm_data);
|
|
return res;
|
|
}
|
|
res = FSFILE_Write(bgm_cache_handle, NULL, 0, bgm_data, 3371008, FS_WRITE_FLUSH);
|
|
free(bgm_data);
|
|
if (R_FAILED(res)) return res;
|
|
|
|
bgm_sizes[i] = bgm_size; // As with body we need the bgm_size for ThemeManage.bin
|
|
FSFILE_Close(bgm_cache_handle);
|
|
FSFILE_Close(bgm_handle);
|
|
} else { // out of themes or this theme was specified not to use BGM
|
|
char *empty = calloc(1, 3371008);
|
|
res = FSFILE_Write(bgm_cache_handle, NULL, 0, empty, 3371008, FS_WRITE_FLUSH);
|
|
free(empty);
|
|
FSFILE_Close(bgm_cache_handle);
|
|
bgm_sizes[i] = 0;
|
|
if (R_FAILED(res)) return res;
|
|
}
|
|
}
|
|
|
|
// Finally we need to write data to ThemeManage.bin
|
|
Handle thememanage_handle;
|
|
char thememanage_buf[0x800];
|
|
res = FSUSER_OpenFile(&thememanage_handle, ArchiveThemeExt, fsMakePath(PATH_ASCII, "/ThemeManage.bin"), FS_OPEN_READ | FS_OPEN_WRITE, 0);
|
|
if (R_FAILED(res)) return res;
|
|
|
|
res = FSFILE_Read(thememanage_handle, NULL, 0, thememanage_buf, 0x800);
|
|
if (R_FAILED(res)) return res;
|
|
|
|
// 3dbrew doesn't know what most of these values do
|
|
thememanage_buf[0x00] = 1; // Unknown, normally 0x1 with size of 4
|
|
thememanage_buf[0x01] = 0;
|
|
thememanage_buf[0x02] = 0;
|
|
thememanage_buf[0x03] = 0;
|
|
thememanage_buf[0x04] = 0; // Unknown, normally 0 with size of 4
|
|
thememanage_buf[0x05] = 0;
|
|
thememanage_buf[0x06] = 0;
|
|
thememanage_buf[0x07] = 0;
|
|
thememanage_buf[0x10] = 0xFF; // Unknown
|
|
thememanage_buf[0x14] = 0x01; // Unkown
|
|
thememanage_buf[0x18] = 0xFF; // DLC theme index - 0xFF indicates no DLC theme
|
|
thememanage_buf[0x1D] = 0x02; // Unknown, usually 0x200 to indicate theme-cache is being used
|
|
|
|
u32 *bodysizeloc = (u32*) (&thememanage_buf[0x08]); // Set non-shuffle theme sizes to 0
|
|
u32 *bgmsizeloc = (u32*) (&thememanage_buf[0x0C]);
|
|
*bodysizeloc = (u32) 0;
|
|
*bgmsizeloc = (u32) 0;
|
|
|
|
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
|
|
*bodysizeloc = body_sizes[i]; // We don't need to check if we've already installed all the themes because all sizes initialized to 0
|
|
*bgmsizeloc = bgm_sizes[i];
|
|
}
|
|
|
|
res = FSFILE_Write(thememanage_handle, NULL, 0, thememanage_buf, 0x800, FS_WRITE_FLUSH);
|
|
if (R_FAILED(res)) return res;
|
|
|
|
FSFILE_Close(res);
|
|
|
|
return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_COMMON, RD_SUCCESS);
|
|
}
|
|
|
|
Result parseSmdh(theme_data *entry, u16 *path)
|
|
{
|
|
Result retValue;
|
|
u32 bytes;
|
|
Handle infoHandle;
|
|
u16 pathToInfo[534] = {0};
|
|
u16 infoPath[12] = {0};
|
|
atow(infoPath, "/info.smdh");
|
|
strucpy(pathToInfo, path);
|
|
strucat(pathToInfo, infoPath);
|
|
char *infoContent = malloc(0x36C0);
|
|
retValue = FSUSER_OpenFile(&infoHandle, ArchiveSD, fsMakePath(PATH_UTF16, pathToInfo), FS_OPEN_READ, 0);
|
|
|
|
if(R_FAILED(retValue))
|
|
{
|
|
free(infoContent);
|
|
return retValue;
|
|
}
|
|
|
|
retValue = FSFILE_Read(infoHandle, &bytes, 0, infoContent, (u32)14016);
|
|
|
|
if(R_FAILED(retValue))
|
|
{
|
|
free(infoContent);
|
|
return retValue;
|
|
}
|
|
|
|
retValue = FSFILE_Close(infoHandle);
|
|
|
|
if(R_FAILED(retValue))
|
|
{
|
|
free(infoContent);
|
|
return retValue;
|
|
}
|
|
|
|
|
|
memcpy(entry->title, infoContent + 0x08, 0x80);
|
|
memcpy(entry->description, infoContent + 0x88, 0x100);
|
|
memcpy(entry->author, infoContent + 0x188, 0x80);
|
|
memcpy(entry->iconData, infoContent + 0x2040, 0x1200);
|
|
|
|
free(infoContent);
|
|
strucpy(entry->path, path);
|
|
return 0;
|
|
}
|
|
|
|
Result closeThemeArchives()
|
|
{
|
|
Result retValue;
|
|
|
|
retValue = FSUSER_CloseArchive(ArchiveSD);
|
|
if(R_FAILED(retValue)) return retValue;
|
|
retValue = FSUSER_CloseArchive(ArchiveHomeExt);
|
|
if(R_FAILED(retValue)) return retValue;
|
|
retValue = FSUSER_CloseArchive(ArchiveThemeExt);
|
|
if(R_FAILED(retValue)) return retValue;
|
|
|
|
return 0;
|
|
}
|