Add support for shuffle (#3)

This commit adds support for theme shuffles.

To use, pass the first node for the theme into the install_shuffle method and it'll automatically install based on what's selected. 

Note that right now, *all themes in /Themes/ are selected for debug purposes.* This means if you have more than 10 themes in /Themes/ it will not work right now. This will be changed once an interface for selecting themes is done.
This commit is contained in:
Alex Taber
2017-07-24 09:10:44 -04:00
committed by GitHub
parent 37aa9022e1
commit 416135bac4
3 changed files with 207 additions and 1 deletions

View File

@@ -31,6 +31,7 @@ int main(void)
splashes_node->data = NULL;
splashes_node->next = NULL;
prepare_splashes(splashes_node);
printf("%li\n", shuffle_install(theme_node));
}
if (kDown & KEY_START)
{

View File

@@ -211,6 +211,13 @@ Result prepareThemes(node *first_node)
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!!!
*/
node *current_theme = malloc(sizeof(node));
current_theme->data = theme_info;
current_theme->next = NULL;
@@ -532,6 +539,202 @@ Result themeInstall(theme_data theme_to_install)
return 0;
}
Result shuffle_install(node *first_node)
{
node *current_node = first_node->next;
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
while (current_node != NULL)
{
if (((theme_data*)current_node->data)->selected) count++;
if (count > 10) return(MAKERESULT(RL_USAGE, RS_INVALIDARG, RM_COMMON, RD_INVALID_SELECTION));
themes_to_be_shuffled[count - 1] = (theme_data *) current_node->data; // -1 because arrays are zero indexed, so the pos is one less than the count
current_node = current_node->next;
}
// 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;

View File

@@ -9,7 +9,8 @@ struct theme_data
u16 description[0x80];
u16 author[0x40];
char iconData[0x1200];
u16 path[533];
u16 path[524];
bool selected;
bool bgm;
};
@@ -26,5 +27,6 @@ FS_Archive ArchiveThemeExt;
Result unzip_file(char*, FS_DirectoryEntry*, u16*);
Result prepareThemes(node*);
Result themeInstall(theme_data);
Result shuffle_install(node*);
Result closeThemeArchives();
Result parseSmdh(theme_data*, u16*);