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:
@@ -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)
|
||||
{
|
||||
|
||||
203
source/theme.c
203
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;
|
||||
|
||||
@@ -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*);
|
||||
|
||||
Reference in New Issue
Block a user