2022-02-04 15:05:17 +01:00
|
|
|
#include "modpackSelector.h"
|
2022-09-18 00:29:12 +02:00
|
|
|
#include "main.h"
|
|
|
|
#include "version.h"
|
2022-09-18 13:04:02 +02:00
|
|
|
#include <content_redirection/redirection.h>
|
|
|
|
#include <coreinit/screen.h>
|
|
|
|
#include <coreinit/thread.h>
|
2021-04-06 16:58:19 +02:00
|
|
|
#include <cstdarg>
|
|
|
|
#include <cstdio>
|
2022-02-04 15:05:17 +01:00
|
|
|
#include <cstring>
|
2022-09-18 13:04:02 +02:00
|
|
|
#include <fs/DirList.h>
|
2021-04-06 16:58:19 +02:00
|
|
|
#include <malloc.h>
|
2022-02-04 15:05:17 +01:00
|
|
|
#include <map>
|
2021-04-06 16:58:19 +02:00
|
|
|
#include <memory/mappedmemory.h>
|
2022-09-18 13:04:02 +02:00
|
|
|
#include <string>
|
2022-02-04 15:05:17 +01:00
|
|
|
#include <utils/logger.h>
|
|
|
|
#include <vpad/input.h>
|
2022-09-18 13:04:02 +02:00
|
|
|
#include <wups/storage.h>
|
2021-04-06 16:58:19 +02:00
|
|
|
|
2022-02-04 15:05:17 +01:00
|
|
|
#define TEXT_SEL(x, text1, text2) ((x) ? (text1) : (text2))
|
2021-04-06 16:58:19 +02:00
|
|
|
|
|
|
|
void HandleMultiModPacks(uint64_t titleID) {
|
|
|
|
char TitleIDString[17];
|
|
|
|
snprintf(TitleIDString, 17, "%016llX", titleID);
|
|
|
|
|
|
|
|
std::map<std::string, std::string> modTitlePath;
|
|
|
|
|
|
|
|
std::map<std::string, std::string> mounting_points;
|
|
|
|
|
2022-09-18 13:04:02 +02:00
|
|
|
const std::string modTitleIDPath = std::string("fs:/vol/external01/sdcafiine/").append(TitleIDString);
|
2022-04-22 17:37:16 +02:00
|
|
|
DirList modTitleDirList(modTitleIDPath, nullptr, DirList::Dirs);
|
2021-04-06 16:58:19 +02:00
|
|
|
|
|
|
|
modTitleDirList.SortList();
|
|
|
|
|
|
|
|
for (int index = 0; index < modTitleDirList.GetFilecount(); index++) {
|
|
|
|
std::string curFile = modTitleDirList.GetFilename(index);
|
2022-04-22 17:37:16 +02:00
|
|
|
|
|
|
|
if (curFile == "." || curFile == "..") {
|
2021-04-06 16:58:19 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:37:16 +02:00
|
|
|
const std::string &packageName = curFile;
|
2022-09-18 13:04:02 +02:00
|
|
|
modTitlePath[packageName] = (modTitleIDPath + "/").append(curFile);
|
|
|
|
DEBUG_FUNCTION_LINE_VERBOSE("Found %s %s", packageName.c_str(), modTitlePath[packageName].c_str());
|
2021-04-06 16:58:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (modTitlePath.empty()) {
|
|
|
|
return;
|
|
|
|
}
|
2022-09-18 13:04:02 +02:00
|
|
|
if (modTitlePath.size() == 1 && gSkipPrepareIfSingleModpack) {
|
|
|
|
ReplaceContent(modTitlePath.begin()->second);
|
|
|
|
return;
|
|
|
|
}
|
2021-04-06 16:58:19 +02:00
|
|
|
|
2022-02-04 15:05:17 +01:00
|
|
|
int selected = 0;
|
2021-04-06 16:58:19 +02:00
|
|
|
int initScreen = 1;
|
2022-02-04 15:05:17 +01:00
|
|
|
int x_offset = -2;
|
2021-04-06 16:58:19 +02:00
|
|
|
|
|
|
|
// Init screen and screen buffers
|
|
|
|
OSScreenInit();
|
|
|
|
uint32_t screen_buf0_size = OSScreenGetBufferSizeEx(SCREEN_TV);
|
|
|
|
uint32_t screen_buf1_size = OSScreenGetBufferSizeEx(SCREEN_DRC);
|
2022-04-22 17:37:16 +02:00
|
|
|
auto *screenBuffer = (uint8_t *) MEMAllocFromMappedMemoryForGX2Ex(screen_buf0_size + screen_buf1_size, 0x100);
|
2021-04-06 16:58:19 +02:00
|
|
|
if (screenBuffer == nullptr) {
|
2022-04-22 17:37:16 +02:00
|
|
|
DEBUG_FUNCTION_LINE_ERR("Failed to alloc screenBuffer");
|
2022-09-18 13:04:02 +02:00
|
|
|
OSFatal("SDCafiine plugin: Failed to alloc screenBuffer.");
|
2021-04-06 16:58:19 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
OSScreenSetBufferEx(SCREEN_TV, (void *) screenBuffer);
|
|
|
|
OSScreenSetBufferEx(SCREEN_DRC, (void *) (screenBuffer + screen_buf0_size));
|
|
|
|
|
|
|
|
OSScreenEnableEx(SCREEN_TV, 1);
|
|
|
|
OSScreenEnableEx(SCREEN_DRC, 1);
|
|
|
|
|
|
|
|
// Clear screens
|
|
|
|
OSScreenClearBufferEx(SCREEN_TV, 0);
|
|
|
|
OSScreenClearBufferEx(SCREEN_DRC, 0);
|
|
|
|
|
|
|
|
OSScreenFlipBuffersEx(SCREEN_TV);
|
|
|
|
OSScreenFlipBuffersEx(SCREEN_DRC);
|
|
|
|
|
|
|
|
VPADStatus vpad_data;
|
|
|
|
VPADReadError error;
|
|
|
|
|
2022-09-18 13:04:02 +02:00
|
|
|
bool displayAutoSkipOption = modTitlePath.size() == 1;
|
|
|
|
|
2021-04-06 16:58:19 +02:00
|
|
|
int wantToExit = 0;
|
2022-02-04 15:05:17 +01:00
|
|
|
int page = 0;
|
2022-09-18 13:04:02 +02:00
|
|
|
int per_page = displayAutoSkipOption ? 11 : 13;
|
2022-02-04 15:05:17 +01:00
|
|
|
int max_pages = (modTitlePath.size() / per_page) + 1;
|
2021-04-06 16:58:19 +02:00
|
|
|
|
2022-09-18 13:04:02 +02:00
|
|
|
int curState = 0;
|
|
|
|
if (gAutoApplySingleModpack && modTitlePath.size() == 1) {
|
|
|
|
curState = 1;
|
|
|
|
}
|
2021-04-06 16:58:19 +02:00
|
|
|
|
2022-09-18 13:04:02 +02:00
|
|
|
int durationInFrames = 60;
|
|
|
|
int frameCounter = 0;
|
|
|
|
|
|
|
|
while (true) {
|
2021-04-06 16:58:19 +02:00
|
|
|
error = VPAD_READ_NO_SAMPLES;
|
|
|
|
VPADRead(VPAD_CHAN_0, &vpad_data, 1, &error);
|
|
|
|
|
2022-09-18 13:04:02 +02:00
|
|
|
if (curState == 1) {
|
|
|
|
if (error == VPAD_READ_SUCCESS) {
|
|
|
|
if (vpad_data.trigger & VPAD_BUTTON_X) {
|
|
|
|
curState = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (initScreen) {
|
|
|
|
OSScreenClearBufferEx(SCREEN_TV, 0);
|
|
|
|
OSScreenClearBufferEx(SCREEN_DRC, 0);
|
|
|
|
console_print_pos(x_offset, -1, "SDCafiine plugin " VERSION VERSION_EXTRA);
|
|
|
|
console_print_pos(x_offset, 1, "Preparing modpack \"%s\"...", modTitlePath.begin()->first.c_str());
|
|
|
|
console_print_pos(x_offset, 3, "Press X to open menu");
|
|
|
|
// Flip buffers
|
|
|
|
OSScreenFlipBuffersEx(SCREEN_TV);
|
|
|
|
OSScreenFlipBuffersEx(SCREEN_DRC);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (frameCounter >= durationInFrames) {
|
|
|
|
ReplaceContent(modTitlePath.begin()->second);
|
2021-04-06 16:58:19 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-09-18 13:04:02 +02:00
|
|
|
frameCounter++;
|
|
|
|
} else {
|
|
|
|
if (error == VPAD_READ_SUCCESS) {
|
|
|
|
if (vpad_data.trigger & VPAD_BUTTON_A) {
|
|
|
|
wantToExit = 1;
|
|
|
|
initScreen = 1;
|
|
|
|
} else if (modTitlePath.size() == 1 && (vpad_data.trigger & VPAD_BUTTON_X)) {
|
|
|
|
OSScreenClearBufferEx(SCREEN_TV, 0);
|
|
|
|
OSScreenClearBufferEx(SCREEN_DRC, 0);
|
|
|
|
|
|
|
|
console_print_pos(x_offset, -1, "SDCafiine plugin " VERSION VERSION_EXTRA);
|
|
|
|
console_print_pos(x_offset, 1, "Save settings...");
|
|
|
|
|
|
|
|
// Flip buffers
|
|
|
|
OSScreenFlipBuffersEx(SCREEN_TV);
|
|
|
|
OSScreenFlipBuffersEx(SCREEN_DRC);
|
|
|
|
|
|
|
|
// We open the storage, so we can persist the configuration the user did.
|
|
|
|
if (WUPS_OpenStorage() == WUPS_STORAGE_ERROR_SUCCESS) {
|
|
|
|
gAutoApplySingleModpack = !gAutoApplySingleModpack;
|
|
|
|
// If the value has changed, we store it in the storage.
|
|
|
|
if (WUPS_StoreInt(nullptr, AUTO_APPLY_SINGLE_MODPACK_STRING, gAutoApplySingleModpack) != WUPS_STORAGE_ERROR_SUCCESS) {
|
|
|
|
}
|
|
|
|
if (WUPS_CloseStorage() != WUPS_STORAGE_ERROR_SUCCESS) {
|
|
|
|
DEBUG_FUNCTION_LINE_ERR("Failed to close storage");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
initScreen = 1;
|
|
|
|
} else if (vpad_data.trigger & VPAD_BUTTON_B) {
|
2021-04-06 16:58:19 +02:00
|
|
|
break;
|
2022-09-18 13:04:02 +02:00
|
|
|
} else if (vpad_data.trigger & VPAD_BUTTON_DOWN) {
|
|
|
|
selected++;
|
|
|
|
initScreen = 1;
|
|
|
|
} else if (vpad_data.trigger & VPAD_BUTTON_UP) {
|
|
|
|
selected--;
|
|
|
|
initScreen = 1;
|
|
|
|
} else if (vpad_data.trigger & VPAD_BUTTON_L) {
|
|
|
|
selected -= per_page;
|
|
|
|
initScreen = 1;
|
|
|
|
} else if (vpad_data.trigger & VPAD_BUTTON_R) {
|
|
|
|
selected += per_page;
|
|
|
|
initScreen = 1;
|
2021-04-06 16:58:19 +02:00
|
|
|
}
|
2022-09-18 13:04:02 +02:00
|
|
|
if (selected < 0) { selected = 0; }
|
|
|
|
if (selected >= modTitlePath.size()) { selected = modTitlePath.size() - 1; }
|
|
|
|
page = selected / per_page;
|
|
|
|
}
|
2021-04-06 16:58:19 +02:00
|
|
|
|
2022-09-18 13:04:02 +02:00
|
|
|
if (initScreen) {
|
|
|
|
OSScreenClearBufferEx(SCREEN_TV, 0);
|
|
|
|
OSScreenClearBufferEx(SCREEN_DRC, 0);
|
|
|
|
console_print_pos(x_offset, -1, "SDCafiine plugin " VERSION VERSION_EXTRA);
|
|
|
|
console_print_pos(x_offset, 1, "Press A to launch a modpack");
|
|
|
|
console_print_pos(x_offset, 2, "Press B to launch without a modpack");
|
|
|
|
if (modTitlePath.size() == 1) {
|
|
|
|
if (gAutoApplySingleModpack) {
|
|
|
|
console_print_pos(x_offset, 4, "Press X to disable autostart for a single modpack");
|
|
|
|
} else {
|
|
|
|
console_print_pos(x_offset, 4, "Press X to enable autostart for a single modpack");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int y_offset = displayAutoSkipOption ? 6 : 4;
|
|
|
|
int cur_ = 0;
|
|
|
|
|
|
|
|
for (auto &it : modTitlePath) {
|
|
|
|
std::string key = it.first;
|
|
|
|
std::string value = it.second;
|
|
|
|
|
|
|
|
if (wantToExit && cur_ == selected) {
|
|
|
|
ReplaceContent(value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cur_ >= (page * per_page) && cur_ < ((page + 1) * per_page)) {
|
|
|
|
console_print_pos(x_offset, y_offset++, "%s %s", TEXT_SEL((selected == cur_), "--->", " "), key.c_str());
|
|
|
|
}
|
|
|
|
cur_++;
|
2021-04-06 16:58:19 +02:00
|
|
|
}
|
|
|
|
|
2022-09-18 13:04:02 +02:00
|
|
|
if (wantToExit) { //just in case.
|
|
|
|
break;
|
|
|
|
}
|
2021-04-06 16:58:19 +02:00
|
|
|
|
2022-09-18 13:04:02 +02:00
|
|
|
if (max_pages > 0) {
|
|
|
|
console_print_pos(x_offset, 17, "Page %02d/%02d. Press L/R to change page.", page + 1, max_pages);
|
|
|
|
}
|
2021-04-06 16:58:19 +02:00
|
|
|
|
2022-09-18 13:04:02 +02:00
|
|
|
// Flip buffers
|
|
|
|
OSScreenFlipBuffersEx(SCREEN_TV);
|
|
|
|
OSScreenFlipBuffersEx(SCREEN_DRC);
|
2021-04-06 16:58:19 +02:00
|
|
|
|
2022-09-18 13:04:02 +02:00
|
|
|
initScreen = 0;
|
|
|
|
}
|
2021-04-06 16:58:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
OSScreenClearBufferEx(SCREEN_TV, 0);
|
|
|
|
OSScreenClearBufferEx(SCREEN_DRC, 0);
|
|
|
|
|
|
|
|
// Flip buffers
|
|
|
|
OSScreenFlipBuffersEx(SCREEN_TV);
|
|
|
|
OSScreenFlipBuffersEx(SCREEN_DRC);
|
|
|
|
|
|
|
|
MEMFreeToMappedMemory(screenBuffer);
|
|
|
|
}
|
2022-04-22 17:37:16 +02:00
|
|
|
extern CRLayerHandle contentLayerHandle;
|
2022-09-18 13:04:02 +02:00
|
|
|
extern CRLayerHandle aocLayerHandle;
|
|
|
|
|
|
|
|
bool ReplaceContentInternal(const std::string &basePath, const std::string &subdir, CRLayerHandle *layerHandle);
|
|
|
|
|
|
|
|
bool ReplaceContent(const std::string &basePath) {
|
|
|
|
bool contentRes = ReplaceContentInternal(basePath, "content", &contentLayerHandle);
|
|
|
|
bool aocRes = ReplaceContentInternal(basePath, "aoc", &aocLayerHandle);
|
2021-04-06 16:58:19 +02:00
|
|
|
|
2022-09-18 13:04:02 +02:00
|
|
|
if (!contentRes && !aocRes) {
|
|
|
|
DEBUG_FUNCTION_LINE_ERR("Failed to apply modpack. Starting without mods.");
|
|
|
|
OSScreenClearBufferEx(SCREEN_TV, 0);
|
|
|
|
OSScreenClearBufferEx(SCREEN_DRC, 0);
|
|
|
|
console_print_pos(-2, -1, "SDCafiine plugin " VERSION VERSION_EXTRA);
|
|
|
|
console_print_pos(-2, 1, "Failed to apply modpack. Starting without mods.");
|
|
|
|
OSScreenFlipBuffersEx(SCREEN_TV);
|
|
|
|
OSScreenFlipBuffersEx(SCREEN_DRC);
|
|
|
|
|
|
|
|
OSSleepTicks(OSMillisecondsToTicks(3000));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ReplaceContentInternal(const std::string &basePath, const std::string &subdir, CRLayerHandle *layerHandle) {
|
|
|
|
std::string layerName = "SDCafiine /vol/" + subdir;
|
|
|
|
std::string fullPath = basePath + "/" + subdir;
|
|
|
|
struct stat st {};
|
|
|
|
if (stat(fullPath.c_str(), &st) < 0) {
|
|
|
|
DEBUG_FUNCTION_LINE_WARN("Skip /vol/%s to %s redirection. Dir does not exist", subdir.c_str(), fullPath.c_str());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto res = ContentRedirection_AddFSLayer(layerHandle,
|
|
|
|
layerName.c_str(),
|
|
|
|
fullPath.c_str(),
|
2022-04-22 17:37:16 +02:00
|
|
|
FS_LAYER_TYPE_CONTENT_MERGE);
|
|
|
|
if (res == CONTENT_REDIRECTION_RESULT_SUCCESS) {
|
2022-09-18 13:04:02 +02:00
|
|
|
DEBUG_FUNCTION_LINE("Redirect /vol/%s to %s", subdir.c_str(), fullPath.c_str());
|
2021-04-06 16:58:19 +02:00
|
|
|
} else {
|
2022-09-18 13:04:02 +02:00
|
|
|
DEBUG_FUNCTION_LINE_ERR("Failed to redirect /vol/%s to %s", subdir.c_str(), fullPath.c_str());
|
|
|
|
|
|
|
|
return false;
|
2021-04-06 16:58:19 +02:00
|
|
|
}
|
2022-09-18 13:04:02 +02:00
|
|
|
return true;
|
2021-04-06 16:58:19 +02:00
|
|
|
}
|
|
|
|
|
2022-09-18 13:04:02 +02:00
|
|
|
|
2021-04-06 16:58:19 +02:00
|
|
|
void console_print_pos(int x, int y, const char *format, ...) {
|
2022-04-22 17:37:16 +02:00
|
|
|
char *tmp = nullptr;
|
2021-04-06 16:58:19 +02:00
|
|
|
|
|
|
|
va_list va;
|
|
|
|
va_start(va, format);
|
|
|
|
if ((vasprintf(&tmp, format, va) >= 0) && tmp) {
|
|
|
|
if (strlen(tmp) > 79) {
|
|
|
|
tmp[79] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
OSScreenPutFontEx(SCREEN_TV, x, y, tmp);
|
|
|
|
OSScreenPutFontEx(SCREEN_DRC, x, y, tmp);
|
|
|
|
}
|
|
|
|
va_end(va);
|
|
|
|
|
|
|
|
if (tmp) {
|
|
|
|
free(tmp);
|
|
|
|
}
|
|
|
|
}
|