diff --git a/source/main.c b/source/main.c index 23e9f76..0d62e7f 100644 --- a/source/main.c +++ b/source/main.c @@ -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) { diff --git a/source/theme.c b/source/theme.c index 5b2a4d1..2b720e0 100644 --- a/source/theme.c +++ b/source/theme.c @@ -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; diff --git a/source/theme.h b/source/theme.h index e179cb7..2d24c51 100644 --- a/source/theme.h +++ b/source/theme.h @@ -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*);