mirror of
https://github.com/J-D-K/JKSV.git
synced 2026-10-25 17:10:26 -05:00
Fix up menus a bit
This commit is contained in:
@@ -11,7 +11,7 @@ namespace data
|
||||
{
|
||||
// Makes stuff easier to type.
|
||||
using titleMap = std::unordered_map<uint64_t, data::titleInfo>;
|
||||
|
||||
|
||||
// Loads users and titles from switch records. This is the initial loading routine and can run as a task.
|
||||
bool init(void);
|
||||
// Loads save data for users
|
||||
|
||||
@@ -53,6 +53,5 @@ namespace data
|
||||
data::userType m_UserType;
|
||||
// Vector of save info for user
|
||||
std::vector<data::userSaveInfo> m_UserSaveInfo;
|
||||
|
||||
};
|
||||
}
|
||||
@@ -11,7 +11,6 @@ namespace data
|
||||
public:
|
||||
// Inits data
|
||||
userSaveInfo(uint64_t titleID, const FsSaveDataInfo &saveDataInfo, const PdmPlayStatistics &playStats);
|
||||
|
||||
// These do not return references or pointers so they can't be changed
|
||||
uint64_t getTitleID(void) const;
|
||||
// Not sure how I feel about returning references, but I don't wanna make copies to get the data from these.
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace fs
|
||||
class directoryListing
|
||||
{
|
||||
public:
|
||||
directoryListing(void) = default;
|
||||
// Gets and stores a listing for directoryPath
|
||||
directoryListing(const std::string &directoryPath);
|
||||
// This is the function that actually loads the directory list. Vector is cleared at beginning.
|
||||
|
||||
@@ -10,7 +10,6 @@ namespace graphics
|
||||
// Inits freetype2
|
||||
bool init(void);
|
||||
void exit(void);
|
||||
|
||||
// Renders text with system font
|
||||
void renderText(const std::string &text, SDL_Texture *target, int x, int y, int fontSize, uint32_t color);
|
||||
// Renders text, but wraps to new line if maxWidth is hit or exeeded
|
||||
|
||||
@@ -48,6 +48,8 @@ namespace ui
|
||||
bool m_ColorShift = true;
|
||||
// Amount to add/subtract from color to pulse
|
||||
int m_ColorMod = 0;
|
||||
// Render target for menu options
|
||||
graphics::sdlTexture m_OptionRenderTarget;
|
||||
|
||||
private:
|
||||
// Vector of options
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace
|
||||
const std::string POP_MESSAGE_SAVE_EMPTY = "popSaveIsEmpty";
|
||||
const std::string CONFIRM_OVERWRITE = "confirmOverwrite";
|
||||
const std::string CONFIRM_RESTORE = "confirmRestore";
|
||||
const std::string CONFIRM_DELETE = "confirmDelete";
|
||||
const std::string CONFIRM_DELETE = "confirmDelete";
|
||||
const std::string BACKUP_MENU_NEW = "folderMenuNew";
|
||||
const std::string SWKBD_PROMPT_TEXT = "swkbdEnterName";
|
||||
}
|
||||
@@ -66,7 +66,7 @@ static void createNewBackup(sys::task *task, sys::sharedTaskData sharedData)
|
||||
|
||||
// Set task status, cast args to backupArgs
|
||||
std::shared_ptr<backupData> dataIn = std::static_pointer_cast<backupData>(sharedData);
|
||||
|
||||
|
||||
// To do: Maybe find a better way to do this without rewriting everything
|
||||
sys::progressTask *progress = reinterpret_cast<sys::progressTask *>(task);
|
||||
|
||||
@@ -183,14 +183,14 @@ static void restoreBackup(sys::task *task, sys::sharedTaskData sharedData)
|
||||
// Get extension before we try to unzip something that isn't a zip
|
||||
std::string fileExtension = stringUtil::getExtensionFromString(dataIn->source);
|
||||
|
||||
if(std::filesystem::is_directory(dataIn->source))
|
||||
if (std::filesystem::is_directory(dataIn->source))
|
||||
{
|
||||
// Add trailing slash
|
||||
std::string source = dataIn->source + "/";
|
||||
// Get a test listing first to prevent writing an empty folder to save
|
||||
fs::directoryListing testListing(source);
|
||||
|
||||
if(testListing.getListingCount() > 0)
|
||||
if (testListing.getListingCount() > 0)
|
||||
{
|
||||
// Folder isn't empty, erase
|
||||
fs::io::deleteDirectoryRecursively(fs::DEFAULT_SAVE_MOUNT_DEVICE);
|
||||
@@ -198,12 +198,12 @@ static void restoreBackup(sys::task *task, sys::sharedTaskData sharedData)
|
||||
fs::io::copyDirectoryCommit(source, fs::DEFAULT_SAVE_MOUNT_DEVICE, dataIn->journalSize, progress);
|
||||
}
|
||||
}
|
||||
else if(std::filesystem::is_directory(dataIn->source) == false && fileExtension == "zip")
|
||||
else if (std::filesystem::is_directory(dataIn->source) == false && fileExtension == "zip")
|
||||
{
|
||||
// Open zip for decompressing
|
||||
unzFile unzip = unzOpen64(dataIn->source.c_str());
|
||||
// Check if opening was successful and not empty first
|
||||
if(unzip != NULL && fs::zip::zipFileIsNotEmpty(unzip))
|
||||
if (unzip != NULL && fs::zip::zipFileIsNotEmpty(unzip))
|
||||
{
|
||||
fs::zip::copyZipToDirectory(unzip, fs::DEFAULT_SAVE_MOUNT_DEVICE, dataIn->journalSize, progress);
|
||||
}
|
||||
@@ -244,22 +244,21 @@ static void deleteBackup(sys::task *task, sys::sharedTaskData sharedData)
|
||||
task->finished();
|
||||
}
|
||||
|
||||
backupMenuState::backupMenuState(data::user *currentUser, data::userSaveInfo *currentUserSaveInfo, data::titleInfo *currentTitleInfo) :
|
||||
m_CurrentUser(currentUser),
|
||||
m_CurrentUserSaveInfo(currentUserSaveInfo),
|
||||
m_CurrentTitleInfo(currentTitleInfo),
|
||||
m_BackupMenuControlGuide(ui::strings::getString(FOLDER_MENU_CONTROL_GUIDE, 0)),
|
||||
m_OutputBasePath(config::getWorkingDirectory() + m_CurrentTitleInfo->getPathSafeTitle() + "/"),
|
||||
m_PanelWidth(graphics::systemFont::getTextWidth(m_BackupMenuControlGuide, 18)),
|
||||
m_BackupPanel(std::make_unique<ui::slidePanel>(BACKUP_PANEL_NAME, m_PanelWidth + 64, ui::PANEL_SIDE_RIGHT)),
|
||||
m_BackupListing(std::make_unique<fs::directoryListing>(m_OutputBasePath)),
|
||||
m_SaveListing(std::make_unique<fs::directoryListing>(fs::DEFAULT_SAVE_MOUNT_DEVICE)),
|
||||
m_BackupMenu(std::make_unique<ui::menu>(10, 4, m_PanelWidth + 44, 18, 6))
|
||||
backupMenuState::backupMenuState(data::user *currentUser, data::userSaveInfo *currentUserSaveInfo, data::titleInfo *currentTitleInfo) : m_CurrentUser(currentUser),
|
||||
m_CurrentUserSaveInfo(currentUserSaveInfo),
|
||||
m_CurrentTitleInfo(currentTitleInfo),
|
||||
m_BackupMenuControlGuide(ui::strings::getString(FOLDER_MENU_CONTROL_GUIDE, 0)),
|
||||
m_OutputBasePath(config::getWorkingDirectory() + m_CurrentTitleInfo->getPathSafeTitle() + "/"),
|
||||
m_PanelWidth(graphics::systemFont::getTextWidth(m_BackupMenuControlGuide, 18)),
|
||||
m_BackupPanel(std::make_unique<ui::slidePanel>(BACKUP_PANEL_NAME, m_PanelWidth + 64, ui::PANEL_SIDE_RIGHT)),
|
||||
m_BackupListing(std::make_unique<fs::directoryListing>(m_OutputBasePath)),
|
||||
m_SaveListing(std::make_unique<fs::directoryListing>(fs::DEFAULT_SAVE_MOUNT_DEVICE)),
|
||||
m_BackupMenu(std::make_unique<ui::menu>(10, 4, m_PanelWidth + 44, 18, 6))
|
||||
{
|
||||
// Make sure path exists
|
||||
std::filesystem::create_directories(m_OutputBasePath);
|
||||
// Render target that menu is rendered to
|
||||
m_BackupMenuRenderTarget = graphics::textureManager::createTexture(BACKUP_MENU_TARGET_NAME, m_PanelWidth + 64, 720, SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET);
|
||||
m_BackupMenuRenderTarget = graphics::textureManager::createTexture(BACKUP_MENU_TARGET_NAME, m_PanelWidth + 64, 647, SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET);
|
||||
// Call this just to be sure.
|
||||
backupMenuState::loadDirectoryList();
|
||||
}
|
||||
@@ -320,7 +319,7 @@ void backupMenuState::update(void)
|
||||
|
||||
// Setup confirmation arg pointer
|
||||
std::shared_ptr<pathData> paths = std::make_shared<pathData>();
|
||||
paths->source = m_BackupListing->getFullPathToItemAt(selected - 1); // Need to subtract one to account for 'New' option
|
||||
paths->source = m_BackupListing->getFullPathToItemAt(selected - 1); // Need to subtract one to account for 'New' option
|
||||
paths->journalSize = m_CurrentTitleInfo->getJournalSize(static_cast<FsSaveDataType>(m_CurrentUserSaveInfo->getSaveDataInfo().save_data_type)); // Needed to prevent commit errors
|
||||
|
||||
// Create confirmation
|
||||
|
||||
@@ -13,36 +13,35 @@ namespace
|
||||
constexpr int HOLD_TICKS = 1000;
|
||||
// Strings used here
|
||||
const std::string DIALOG_YES = "dialogYes";
|
||||
const std::string DIALOG_NO = "dialogNo";
|
||||
const std::string DIALOG_NO = "dialogNo";
|
||||
const std::string HOLDING_TEXT = "holdingText";
|
||||
}
|
||||
|
||||
confirmState::confirmState(const std::string &message, sys::taskFunction onConfirmation, sys::sharedTaskData sharedData, sys::taskTypes taskType) :
|
||||
m_Message(message),
|
||||
m_Yes(ui::strings::getString(DIALOG_YES, 0)),
|
||||
m_No(ui::strings::getString(DIALOG_NO, 0)),
|
||||
m_HoldTimer(std::make_unique<sys::timer>(HOLD_TICKS)),
|
||||
m_OnConfirmation(onConfirmation),
|
||||
m_SharedData(sharedData),
|
||||
m_TaskType(taskType) { }
|
||||
confirmState::confirmState(const std::string &message, sys::taskFunction onConfirmation, sys::sharedTaskData sharedData, sys::taskTypes taskType) : m_Message(message),
|
||||
m_Yes(ui::strings::getString(DIALOG_YES, 0)),
|
||||
m_No(ui::strings::getString(DIALOG_NO, 0)),
|
||||
m_HoldTimer(std::make_unique<sys::timer>(HOLD_TICKS)),
|
||||
m_OnConfirmation(onConfirmation),
|
||||
m_SharedData(sharedData),
|
||||
m_TaskType(taskType) {}
|
||||
|
||||
confirmState::~confirmState() { }
|
||||
confirmState::~confirmState() {}
|
||||
|
||||
// To do: I don't like the way this looks, but not sure if it can get much better
|
||||
void confirmState::update(void)
|
||||
{
|
||||
// If A is first pressed, and stage is 0, restart timer
|
||||
if(sys::input::buttonDown(HidNpadButton_A) && m_HoldStage == 0)
|
||||
if (sys::input::buttonDown(HidNpadButton_A) && m_HoldStage == 0)
|
||||
{
|
||||
m_HoldTimer->restartTimer();
|
||||
}
|
||||
else if(sys::input::buttonHeld(HidNpadButton_A) && m_HoldStage < 3 && m_HoldTimer->triggered())
|
||||
else if (sys::input::buttonHeld(HidNpadButton_A) && m_HoldStage < 3 && m_HoldTimer->triggered())
|
||||
{
|
||||
// Confirmed, create new task.
|
||||
if(++m_HoldStage == 3)
|
||||
if (++m_HoldStage == 3)
|
||||
{
|
||||
// Spawn task according to m_TaskType
|
||||
switch(m_TaskType)
|
||||
switch (m_TaskType)
|
||||
{
|
||||
case sys::taskTypes::TASK_TYPE_TASK:
|
||||
{
|
||||
@@ -65,30 +64,30 @@ void confirmState::update(void)
|
||||
switch (m_HoldStage)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
m_Yes = ui::strings::getString(HOLDING_TEXT, 0);
|
||||
}
|
||||
break;
|
||||
{
|
||||
m_Yes = ui::strings::getString(HOLDING_TEXT, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
{
|
||||
m_Yes = ui::strings::getString(HOLDING_TEXT, 1);
|
||||
}
|
||||
break;
|
||||
{
|
||||
m_Yes = ui::strings::getString(HOLDING_TEXT, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
{
|
||||
m_Yes = ui::strings::getString(HOLDING_TEXT, 2);
|
||||
}
|
||||
break;
|
||||
{
|
||||
m_Yes = ui::strings::getString(HOLDING_TEXT, 2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(sys::input::buttonReleased(HidNpadButton_A))
|
||||
else if (sys::input::buttonReleased(HidNpadButton_A))
|
||||
{
|
||||
m_Yes = ui::strings::getString(DIALOG_YES, 0);
|
||||
}
|
||||
else if(sys::input::buttonDown(HidNpadButton_B))
|
||||
else if (sys::input::buttonDown(HidNpadButton_B))
|
||||
{
|
||||
// Party's over. Just stop
|
||||
appState::deactivateState();
|
||||
|
||||
@@ -41,14 +41,14 @@ static void backupAllUserSaves(sys::task *task, sys::sharedTaskData sharedData)
|
||||
|
||||
// Loop through users
|
||||
int totalUserCount = data::getTotalUsers();
|
||||
for(int i = 0; i < totalUserCount; i++)
|
||||
for (int i = 0; i < totalUserCount; i++)
|
||||
{
|
||||
// Get pointer to user
|
||||
data::user *currentUser = data::getUserAtPosition(i);
|
||||
|
||||
// Loop through titles
|
||||
int totalUserGameCount = currentUser->getTotalUserSaveInfo();
|
||||
for(int j = 0; j < totalUserGameCount; j++)
|
||||
for (int j = 0; j < totalUserGameCount; j++)
|
||||
{
|
||||
// Get pointer to userSaveInfo
|
||||
data::userSaveInfo *currentUserSaveInfo = currentUser->getUserSaveInfoAt(j);
|
||||
@@ -60,20 +60,20 @@ static void backupAllUserSaves(sys::task *task, sys::sharedTaskData sharedData)
|
||||
bool saveIsMounted = fs::mountSaveData(currentUserSaveInfo->getSaveDataInfo());
|
||||
// Make sure full path for title exists
|
||||
fs::createTitleDirectoryByTID(currentUserSaveInfo->getTitleID());
|
||||
if(saveIsMounted && fs::directoryContainsFiles(fs::DEFAULT_SAVE_MOUNT_DEVICE) && config::getByKey(CONFIG_USE_ZIP))
|
||||
if (saveIsMounted && fs::directoryContainsFiles(fs::DEFAULT_SAVE_MOUNT_DEVICE) && config::getByKey(CONFIG_USE_ZIP))
|
||||
{
|
||||
// Path to zip we're backing up. It's a biggin'
|
||||
std::string outputZipPath = config::getWorkingDirectory() + currentTitleInfo->getPathSafeTitle() + "/" + currentUser->getPathSafeUsername() + \
|
||||
std::string outputZipPath = config::getWorkingDirectory() + currentTitleInfo->getPathSafeTitle() + "/" + currentUser->getPathSafeUsername() +
|
||||
" - " + stringUtil::getTimeAndDateString(stringUtil::DATE_FORMAT_YMD) + ".zip";
|
||||
// Open zip
|
||||
zipFile newZip = zipOpen64(outputZipPath.c_str(), 0);
|
||||
// Copy to it
|
||||
fs::zip::copyDirectoryToZip(fs::DEFAULT_SAVE_MOUNT_DEVICE, newZip, progress);
|
||||
}
|
||||
else if(saveIsMounted && fs::directoryContainsFiles(fs::DEFAULT_SAVE_MOUNT_DEVICE) && config::getByKey(CONFIG_USE_ZIP) == false)
|
||||
else if (saveIsMounted && fs::directoryContainsFiles(fs::DEFAULT_SAVE_MOUNT_DEVICE) && config::getByKey(CONFIG_USE_ZIP) == false)
|
||||
{
|
||||
// Output path
|
||||
std::string newSaveBackupPath = config::getWorkingDirectory() + currentTitleInfo->getPathSafeTitle() + "/" + currentUser->getPathSafeUsername() + \
|
||||
std::string newSaveBackupPath = config::getWorkingDirectory() + currentTitleInfo->getPathSafeTitle() + "/" + currentUser->getPathSafeUsername() +
|
||||
" - " + stringUtil::getTimeAndDateString(stringUtil::DATE_FORMAT_YMD) + "/";
|
||||
// Make sure it exists
|
||||
std::filesystem::create_directories(newSaveBackupPath);
|
||||
@@ -86,10 +86,9 @@ static void backupAllUserSaves(sys::task *task, sys::sharedTaskData sharedData)
|
||||
progress->finished();
|
||||
}
|
||||
|
||||
mainMenuState::mainMenuState(void) :
|
||||
m_MainControlGuide(ui::strings::getString(MAIN_MENU_CONTROL_GUIDE, 0)),
|
||||
m_MainControlGuideX(1220 - graphics::systemFont::getTextWidth(m_MainControlGuide, 18)),
|
||||
m_MainMenu(std::make_unique<ui::iconMenu>(50, 16, 1))
|
||||
mainMenuState::mainMenuState(void) : m_MainControlGuide(ui::strings::getString(MAIN_MENU_CONTROL_GUIDE, 0)),
|
||||
m_MainControlGuideX(1220 - graphics::systemFont::getTextWidth(m_MainControlGuide, 18)),
|
||||
m_MainMenu(std::make_unique<ui::iconMenu>(50, 16, 1))
|
||||
{
|
||||
// Render target
|
||||
m_RenderTarget = graphics::textureManager::createTexture(MAIN_MENU_RENDER_TARGET, 200, 555, SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET);
|
||||
@@ -119,11 +118,11 @@ void mainMenuState::update(void)
|
||||
m_MainMenu->update();
|
||||
|
||||
int selected = m_MainMenu->getSelected();
|
||||
if(sys::input::buttonDown(HidNpadButton_A) && selected < m_UserEnd)
|
||||
if (sys::input::buttonDown(HidNpadButton_A) && selected < m_UserEnd)
|
||||
{
|
||||
data::user *selectedUser = data::getUserAtPosition(selected);
|
||||
// Normally wouldn't do this
|
||||
if(selectedUser->getTotalUserSaveInfo() > 0)
|
||||
if (selectedUser->getTotalUserSaveInfo() > 0)
|
||||
{
|
||||
std::unique_ptr<appState> titleSelection = std::make_unique<titleSelectionState>(selectedUser);
|
||||
jksv::pushNewState(titleSelection);
|
||||
@@ -134,12 +133,12 @@ void mainMenuState::update(void)
|
||||
ui::popMessage::newMessage(noSavesMessage, ui::popMessage::POPMESSAGE_DEFAULT_TICKS);
|
||||
}
|
||||
}
|
||||
else if(sys::input::buttonDown(HidNpadButton_Y))
|
||||
else if (sys::input::buttonDown(HidNpadButton_Y))
|
||||
{
|
||||
// This doesn't need any input
|
||||
createAndPushNewProgressState(backupAllUserSaves, nullptr);
|
||||
}
|
||||
else if(sys::input::buttonDown(HidNpadButton_X) && selected < m_UserEnd)
|
||||
else if (sys::input::buttonDown(HidNpadButton_X) && selected < m_UserEnd)
|
||||
{
|
||||
// Get selected user
|
||||
data::user *selectedUser = data::getUserAtPosition(selected);
|
||||
@@ -159,7 +158,7 @@ void mainMenuState::render(void)
|
||||
// Render render target to framebuffer
|
||||
graphics::textureRender(m_RenderTarget.get(), NULL, 0, 91);
|
||||
|
||||
if(appState::hasFocus())
|
||||
if (appState::hasFocus())
|
||||
{
|
||||
graphics::systemFont::renderText(m_MainControlGuide, NULL, m_MainControlGuideX, 673, 18, COLOR_WHITE);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace
|
||||
const double PROG_BAR_WIDTH = 648.0f;
|
||||
const int PROG_BAR_X = 312;
|
||||
const int PROG_BAR_Y = 471;
|
||||
//static const int PROG_BAR_WIDTH = 484; Where'd this come from? Try to figure it out later...
|
||||
// static const int PROG_BAR_WIDTH = 484; Where'd this come from? Try to figure it out later...
|
||||
const int PROG_BAR_HEIGHT = 32;
|
||||
// These are for the status string
|
||||
const int STATUS_TEXT_X = PROG_DIALOG_X + 16;
|
||||
@@ -27,14 +27,13 @@ namespace
|
||||
const int PROGRESS_STATUS_FONT_SIZE = 18;
|
||||
}
|
||||
|
||||
progressState::progressState(sys::taskFunction threadFunction, sys::sharedTaskData sharedData) :
|
||||
m_Task(std::make_unique<sys::progressTask>(threadFunction, sharedData)) { }
|
||||
progressState::progressState(sys::taskFunction threadFunction, sys::sharedTaskData sharedData) : m_Task(std::make_unique<sys::progressTask>(threadFunction, sharedData)) {}
|
||||
|
||||
progressState::~progressState() { }
|
||||
progressState::~progressState() {}
|
||||
|
||||
void progressState::update(void)
|
||||
{
|
||||
if(m_Task->isRunning() == false)
|
||||
if (m_Task->isRunning() == false)
|
||||
{
|
||||
appState::deactivateState();
|
||||
}
|
||||
@@ -63,7 +62,7 @@ void progressState::render()
|
||||
graphics::renderRect(NULL, PROG_BAR_X, PROG_BAR_Y, PROG_BAR_WIDTH, PROG_BAR_HEIGHT, COLOR_BLACK);
|
||||
graphics::renderRect(NULL, PROG_BAR_X, PROG_BAR_Y, m_BarWidth, PROG_BAR_HEIGHT, COLOR_GREEN);
|
||||
graphics::systemFont::renderTextWrap(m_TaskStatus, NULL, STATUS_TEXT_X, STATUS_TEXT_Y, STATUS_TEXT_FONT_SIZE, STATUS_TEXT_MAX_WIDTH, COLOR_WHITE); // Status
|
||||
graphics::systemFont::renderText(m_ProgressStatus, NULL, m_ProgressStatusX, PROGRESS_STATUS_Y, PROGRESS_STATUS_FONT_SIZE, COLOR_WHITE); // Actual progress
|
||||
graphics::systemFont::renderText(m_ProgressStatus, NULL, m_ProgressStatusX, PROGRESS_STATUS_Y, PROGRESS_STATUS_FONT_SIZE, COLOR_WHITE); // Actual progress
|
||||
}
|
||||
|
||||
void createAndPushNewProgressState(sys::taskFunction threadFunction, sys::sharedTaskData sharedData)
|
||||
|
||||
@@ -12,14 +12,13 @@ namespace
|
||||
static const int STATUS_TEXT_FONT_SIZE = 24;
|
||||
}
|
||||
|
||||
taskState::taskState(sys::taskFunction threadFunction, sys::sharedTaskData sharedData) :
|
||||
m_Task(std::make_unique<sys::task>(threadFunction, sharedData)) { }
|
||||
taskState::taskState(sys::taskFunction threadFunction, sys::sharedTaskData sharedData) : m_Task(std::make_unique<sys::task>(threadFunction, sharedData)) {}
|
||||
|
||||
taskState::~taskState() { }
|
||||
taskState::~taskState() {}
|
||||
|
||||
void taskState::update(void)
|
||||
{
|
||||
if(m_Task->isRunning() == false)
|
||||
if (m_Task->isRunning() == false)
|
||||
{
|
||||
appState::deactivateState();
|
||||
}
|
||||
@@ -33,7 +32,7 @@ void taskState::render(void)
|
||||
|
||||
// Render glyph in bottom left corner
|
||||
ui::renderLoadingGlyph();
|
||||
|
||||
|
||||
// Dim background and render text to screen
|
||||
graphics::renderRect(NULL, 0, 0, 1280, 720, COLOR_DIM_BACKGROUND);
|
||||
graphics::systemFont::renderText(taskStatus, NULL, taskStatusX, STATUS_TEXT_Y, STATUS_TEXT_FONT_SIZE, COLOR_WHITE);
|
||||
|
||||
@@ -57,7 +57,7 @@ struct titleOptsData : sys::taskData
|
||||
// This was needed to debug something. Only one for now.
|
||||
void resetSaveData(sys::task *task, sys::sharedTaskData sharedData)
|
||||
{
|
||||
if(sharedData == nullptr)
|
||||
if (sharedData == nullptr)
|
||||
{
|
||||
task->finished();
|
||||
return;
|
||||
@@ -68,11 +68,11 @@ void resetSaveData(sys::task *task, sys::sharedTaskData sharedData)
|
||||
|
||||
// Set status
|
||||
task->setThreadStatus(ui::strings::getString(THREAD_STATUS_RESETTING_SAVE, 0));
|
||||
|
||||
|
||||
// Cast args
|
||||
std::shared_ptr<titleOptsData> dataIn = std::static_pointer_cast<titleOptsData>(sharedData);
|
||||
|
||||
if(fs::mountSaveData(dataIn->currentUserSaveInfo->getSaveDataInfo()))
|
||||
if (fs::mountSaveData(dataIn->currentUserSaveInfo->getSaveDataInfo()))
|
||||
{
|
||||
// Erase save data in container
|
||||
// To do: remove_all keeps failing if the folder has sub dirs. Maybe I should just stick with my own?
|
||||
@@ -94,21 +94,20 @@ void resetSaveData(sys::task *task, sys::sharedTaskData sharedData)
|
||||
task->finished();
|
||||
}
|
||||
|
||||
titleOptionState::titleOptionState(data::user *currentUser, data::userSaveInfo *currentUserSaveInfo, data::titleInfo *currentTitleInfo) :
|
||||
m_CurrentUser(currentUser),
|
||||
m_CurrentUserSaveInfo(currentUserSaveInfo),
|
||||
m_CurrentTitleInfo(currentTitleInfo),
|
||||
m_SlidePanel(std::make_unique<ui::slidePanel>(PANEL_RENDER_TARGET_NAME, PANEL_WIDTH, ui::PANEL_SIDE_RIGHT)),
|
||||
m_OptionsMenu(std::make_unique<ui::menu>(MENU_X, MENU_Y, MENU_WIDTH, MENU_FONT_SIZE, MENU_SCROLL_LENGTH))
|
||||
titleOptionState::titleOptionState(data::user *currentUser, data::userSaveInfo *currentUserSaveInfo, data::titleInfo *currentTitleInfo) : m_CurrentUser(currentUser),
|
||||
m_CurrentUserSaveInfo(currentUserSaveInfo),
|
||||
m_CurrentTitleInfo(currentTitleInfo),
|
||||
m_SlidePanel(std::make_unique<ui::slidePanel>(PANEL_RENDER_TARGET_NAME, PANEL_WIDTH, ui::PANEL_SIDE_RIGHT)),
|
||||
m_OptionsMenu(std::make_unique<ui::menu>(MENU_X, MENU_Y, MENU_WIDTH, MENU_FONT_SIZE, MENU_SCROLL_LENGTH))
|
||||
{
|
||||
// Setup menu
|
||||
for(int i = 0; i < 9; i++)
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
m_OptionsMenu->addOption(ui::strings::getString(TITLE_OPTIONS_MENU_STRINGS, i));
|
||||
}
|
||||
}
|
||||
|
||||
titleOptionState::~titleOptionState() { }
|
||||
titleOptionState::~titleOptionState() {}
|
||||
|
||||
void titleOptionState::update(void)
|
||||
{
|
||||
@@ -116,9 +115,9 @@ void titleOptionState::update(void)
|
||||
m_SlidePanel->update();
|
||||
m_OptionsMenu->update();
|
||||
|
||||
if(sys::input::buttonDown(HidNpadButton_A))
|
||||
if (sys::input::buttonDown(HidNpadButton_A))
|
||||
{
|
||||
switch(m_OptionsMenu->getSelected())
|
||||
switch (m_OptionsMenu->getSelected())
|
||||
{
|
||||
// This is needed to debug something... and itself.
|
||||
case RESET_SAVE_DATA:
|
||||
@@ -142,11 +141,11 @@ void titleOptionState::update(void)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(sys::input::buttonDown(HidNpadButton_B))
|
||||
else if (sys::input::buttonDown(HidNpadButton_B))
|
||||
{
|
||||
m_SlidePanel->closePanel();
|
||||
}
|
||||
else if(m_SlidePanel->isClosed())
|
||||
else if (m_SlidePanel->isClosed())
|
||||
{
|
||||
appState::deactivateState();
|
||||
}
|
||||
|
||||
@@ -18,11 +18,10 @@ namespace
|
||||
const std::string TITLE_SELECTION_CONTROL_GUIDE = "helpTitle";
|
||||
}
|
||||
|
||||
titleSelectionState::titleSelectionState(data::user *currentUser) :
|
||||
m_CurrentUser(currentUser),
|
||||
m_TitleSelection(std::make_unique<ui::titleSelection>(m_CurrentUser)),
|
||||
m_TitleControlGuide(ui::strings::getString(TITLE_SELECTION_CONTROL_GUIDE, 0)),
|
||||
m_TitleControlGuideX(1220 - graphics::systemFont::getTextWidth(m_TitleControlGuide, 18))
|
||||
titleSelectionState::titleSelectionState(data::user *currentUser) : m_CurrentUser(currentUser),
|
||||
m_TitleSelection(std::make_unique<ui::titleSelection>(m_CurrentUser)),
|
||||
m_TitleControlGuide(ui::strings::getString(TITLE_SELECTION_CONTROL_GUIDE, 0)),
|
||||
m_TitleControlGuideX(1220 - graphics::systemFont::getTextWidth(m_TitleControlGuide, 18))
|
||||
{
|
||||
// Render target to render everything too
|
||||
m_RenderTarget = graphics::textureManager::createTexture(TITLE_SELECT_RENDER_TARGET, 1080, 555, SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET);
|
||||
@@ -34,13 +33,13 @@ void titleSelectionState::update(void)
|
||||
{
|
||||
m_TitleSelection->update();
|
||||
|
||||
if(sys::input::buttonDown(HidNpadButton_A))
|
||||
if (sys::input::buttonDown(HidNpadButton_A))
|
||||
{
|
||||
int selected = m_TitleSelection->getSelected();
|
||||
data::userSaveInfo *currentUserSaveInfo = m_CurrentUser->getUserSaveInfoAt(selected);
|
||||
if(fs::mountSaveData(currentUserSaveInfo->getSaveDataInfo()))
|
||||
if (fs::mountSaveData(currentUserSaveInfo->getSaveDataInfo()))
|
||||
{
|
||||
data::titleInfo *currentTitleInfo = data::getTitleInfoByTitleID(currentUserSaveInfo->getTitleID());
|
||||
data::titleInfo *currentTitleInfo = data::getTitleInfoByTitleID(currentUserSaveInfo->getTitleID());
|
||||
std::unique_ptr<appState> backupState = std::make_unique<backupMenuState>(m_CurrentUser, currentUserSaveInfo, currentTitleInfo);
|
||||
jksv::pushNewState(backupState);
|
||||
}
|
||||
@@ -49,7 +48,7 @@ void titleSelectionState::update(void)
|
||||
logger::log("Error mounting save for %016lX", currentUserSaveInfo->getTitleID());
|
||||
}
|
||||
}
|
||||
else if(sys::input::buttonDown(HidNpadButton_X))
|
||||
else if (sys::input::buttonDown(HidNpadButton_X))
|
||||
{
|
||||
// Get some stuff needed
|
||||
int selected = m_TitleSelection->getSelected();
|
||||
@@ -61,7 +60,7 @@ void titleSelectionState::update(void)
|
||||
// Push it
|
||||
jksv::pushNewState(optionState);
|
||||
}
|
||||
else if(sys::input::buttonDown(HidNpadButton_B))
|
||||
else if (sys::input::buttonDown(HidNpadButton_B))
|
||||
{
|
||||
appState::deactivateState();
|
||||
}
|
||||
@@ -77,7 +76,7 @@ void titleSelectionState::render(void)
|
||||
// Render to screen
|
||||
graphics::textureRender(m_RenderTarget.get(), NULL, 201, 91);
|
||||
|
||||
if(appState::hasFocus())
|
||||
if (appState::hasFocus())
|
||||
{
|
||||
graphics::systemFont::renderText(m_TitleControlGuide, NULL, m_TitleControlGuideX, 673, 18, COLOR_WHITE);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "appStates/userOptionState.hpp"
|
||||
#include "appStates/progressState.hpp"
|
||||
#include "appStates/saveCreateState.hpp"
|
||||
#include "appStates/confirmState.hpp"
|
||||
#include "system/progressTask.hpp"
|
||||
#include "filesystem/filesystem.hpp"
|
||||
#include "jksv.hpp"
|
||||
@@ -17,6 +18,7 @@ namespace
|
||||
const std::string OPTION_MENU_STRING_NAME = "userOptions";
|
||||
const std::string THREAD_STATUS_CREATING_SAVE_DATA = "threadStatusCreatingSaveData";
|
||||
const std::string THREAD_STATUS_DELETING_SAVE_DATA = "threadStatusDeletingSaveData";
|
||||
const std::string CONFIRM_DELETE_ALL_USER_SAVES = "saveDataDeleteAllUser";
|
||||
// Enum for menu opts
|
||||
enum
|
||||
{
|
||||
@@ -81,7 +83,7 @@ void dumpAllForUser(sys::task *task, sys::sharedTaskData sharedData)
|
||||
|
||||
void createAllSaveDataForUser(sys::task *task, sys::sharedTaskData sharedData)
|
||||
{
|
||||
if(sharedData == nullptr)
|
||||
if (sharedData == nullptr)
|
||||
{
|
||||
task->finished();
|
||||
return;
|
||||
@@ -94,9 +96,9 @@ void createAllSaveDataForUser(sys::task *task, sys::sharedTaskData sharedData)
|
||||
data::titleMap &titleMap = data::getTitleMap();
|
||||
|
||||
// Loop through map. Have to be careful though.
|
||||
for(auto ¤tTitle : titleMap)
|
||||
for (auto ¤tTitle : titleMap)
|
||||
{
|
||||
if(dataIn->currentUser->getUserType() == data::userType::TYPE_USER && currentTitle.second.hasAccountSaveData())
|
||||
if (dataIn->currentUser->getUserType() == data::userType::TYPE_USER && currentTitle.second.hasAccountSaveData())
|
||||
{
|
||||
// Set status string.
|
||||
std::string threadStatus = stringUtil::getFormattedString(ui::strings::getCString(THREAD_STATUS_CREATING_SAVE_DATA, 0), currentTitle.second.getTitle().c_str());
|
||||
@@ -114,7 +116,7 @@ void createAllSaveDataForUser(sys::task *task, sys::sharedTaskData sharedData)
|
||||
|
||||
void deleteAllSaveDataForUser(sys::task *task, sys::sharedTaskData sharedData)
|
||||
{
|
||||
if(sharedData == nullptr)
|
||||
if (sharedData == nullptr)
|
||||
{
|
||||
task->finished();
|
||||
return;
|
||||
@@ -124,7 +126,7 @@ void deleteAllSaveDataForUser(sys::task *task, sys::sharedTaskData sharedData)
|
||||
|
||||
// Loop through and nuke them
|
||||
int totalUserSaves = dataIn->currentUser->getTotalUserSaveInfo();
|
||||
for(int i = 0; i < totalUserSaves; i++)
|
||||
for (int i = 0; i < totalUserSaves; i++)
|
||||
{
|
||||
// Save info is needed first for titleID
|
||||
data::userSaveInfo *currentUserSaveInfo = dataIn->currentUser->getUserSaveInfoAt(i);
|
||||
@@ -171,60 +173,75 @@ void userOptionState::update(void)
|
||||
{
|
||||
switch (m_OptionsMenu->getSelected())
|
||||
{
|
||||
case OPTION_BACKUP_ALL_FOR_USER:
|
||||
{
|
||||
// Data to send
|
||||
std::shared_ptr<userOptionData> optionsData = std::make_shared<userOptionData>();
|
||||
optionsData->currentUser = m_CurrentUser;
|
||||
// Push new state/task
|
||||
createAndPushNewProgressState(dumpAllForUser, optionsData);
|
||||
}
|
||||
break;
|
||||
case OPTION_BACKUP_ALL_FOR_USER:
|
||||
{
|
||||
// Data to send
|
||||
std::shared_ptr<userOptionData> optionsData = std::make_shared<userOptionData>();
|
||||
optionsData->currentUser = m_CurrentUser;
|
||||
// Push new state/task
|
||||
createAndPushNewProgressState(dumpAllForUser, optionsData);
|
||||
}
|
||||
break;
|
||||
|
||||
case OPTION_CREATE_SAVE_DATA:
|
||||
case OPTION_CREATE_SAVE_DATA:
|
||||
{
|
||||
// What save type we're working with. Using UserID to figure it out.
|
||||
FsSaveDataType saveDataType;
|
||||
switch (m_CurrentUser->getAccountIDU128())
|
||||
{
|
||||
// What save type we're working with. Using UserID to figure it out.
|
||||
FsSaveDataType saveDataType;
|
||||
if (m_CurrentUser->getAccountIDU128() == 2)
|
||||
case 2:
|
||||
{
|
||||
saveDataType = FsSaveDataType_Bcat;
|
||||
}
|
||||
else if (m_CurrentUser->getAccountIDU128() == 3)
|
||||
break;
|
||||
|
||||
case 3:
|
||||
{
|
||||
saveDataType = FsSaveDataType_Device;
|
||||
}
|
||||
else if (m_CurrentUser->getAccountIDU128() == 5)
|
||||
break;
|
||||
|
||||
case 5:
|
||||
{
|
||||
saveDataType = FsSaveDataType_Cache;
|
||||
}
|
||||
else
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
saveDataType = FsSaveDataType_Account;
|
||||
}
|
||||
|
||||
// Push the state
|
||||
std::unique_ptr<appState> saveCreationState = std::make_unique<saveCreateState>(m_CurrentUser, saveDataType);
|
||||
jksv::pushNewState(saveCreationState);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case OPTION_CREATE_ALL_SAVE_DATA: // This needs to be fixed up...
|
||||
{
|
||||
std::shared_ptr<userOptionData> userData = std::make_shared<userOptionData>();
|
||||
userData->currentUser = m_CurrentUser;
|
||||
// Push the state
|
||||
std::unique_ptr<appState> saveCreationState = std::make_unique<saveCreateState>(m_CurrentUser, saveDataType);
|
||||
jksv::pushNewState(saveCreationState);
|
||||
}
|
||||
break;
|
||||
|
||||
createAndPushNewTask(createAllSaveDataForUser, userData);
|
||||
}
|
||||
break;
|
||||
case OPTION_CREATE_ALL_SAVE_DATA: // This needs to be fixed up...
|
||||
{
|
||||
std::shared_ptr<userOptionData> userData = std::make_shared<userOptionData>();
|
||||
userData->currentUser = m_CurrentUser;
|
||||
|
||||
case OPTION_DELETE_ALL_SAVES_FOR_USER:
|
||||
{
|
||||
std::shared_ptr<userOptionData> userData = std::make_shared<userOptionData>();
|
||||
userData->currentUser = m_CurrentUser;
|
||||
createAndPushNewTask(createAllSaveDataForUser, userData);
|
||||
}
|
||||
break;
|
||||
|
||||
createAndPushNewTask(deleteAllSaveDataForUser, userData);
|
||||
}
|
||||
break;
|
||||
case OPTION_DELETE_ALL_SAVES_FOR_USER:
|
||||
{
|
||||
// Create confirmation string
|
||||
std::string confirmDeleteAll = stringUtil::getFormattedString(ui::strings::getCString(CONFIRM_DELETE_ALL_USER_SAVES, 0), m_CurrentUser->getUsername().c_str());
|
||||
|
||||
// Shared data
|
||||
std::shared_ptr<userOptionData> userData = std::make_shared<userOptionData>();
|
||||
userData->currentUser = m_CurrentUser;
|
||||
|
||||
// Confirm it first.
|
||||
confirmAction(confirmDeleteAll, deleteAllSaveDataForUser, userData, sys::taskTypes::TASK_TYPE_TASK);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (sys::input::buttonDown(HidNpadButton_B))
|
||||
|
||||
@@ -42,14 +42,15 @@ static u128 accountUIDToU128(AccountUid accountID)
|
||||
// Same as above, but reversed
|
||||
static AccountUid u128ToAccountUID(u128 accountID)
|
||||
{
|
||||
return (AccountUid){ (uint64_t)(accountID >> 64), (uint64_t)accountID };
|
||||
return (AccountUid){(uint64_t)(accountID >> 64), (uint64_t)accountID};
|
||||
}
|
||||
|
||||
// Checks if user exists in vector
|
||||
static bool userExistsInVector(u128 accountID)
|
||||
{
|
||||
// This is kind of hard to follow, but search the vector and see if accountID exists
|
||||
auto findUser = std::find_if(s_UserVector.begin(), s_UserVector.end(), [accountID] ( const userIDPair &userPair ) { return userPair.first == accountID; } );
|
||||
auto findUser = std::find_if(s_UserVector.begin(), s_UserVector.end(), [accountID](const userIDPair &userPair)
|
||||
{ return userPair.first == accountID; });
|
||||
return findUser != s_UserVector.end();
|
||||
}
|
||||
|
||||
@@ -74,7 +75,7 @@ static bool isAccountSystemSave(const FsSaveDataInfo &saveDataInfo)
|
||||
{
|
||||
uint8_t saveDataType = saveDataInfo.save_data_type;
|
||||
u128 accountID = accountUIDToU128(saveDataInfo.uid);
|
||||
if(saveDataType == FsSaveDataType_System && accountID != 0)
|
||||
if (saveDataType == FsSaveDataType_System && accountID != 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -114,7 +115,7 @@ bool data::init(void)
|
||||
NsApplicationRecord currentRecord;
|
||||
int entryCount = 0;
|
||||
int entryOffset = 0;
|
||||
while(R_SUCCEEDED(nsListApplicationRecord(¤tRecord, 1, entryOffset++, &entryCount)) && entryCount > 0)
|
||||
while (R_SUCCEEDED(nsListApplicationRecord(¤tRecord, 1, entryOffset++, &entryCount)) && entryCount > 0)
|
||||
{
|
||||
// This is logged for when JKSV gets stuck on things
|
||||
logger::log("Add title 0x%016lX.", currentRecord.application_id);
|
||||
@@ -127,7 +128,7 @@ bool data::init(void)
|
||||
void data::loadUserSaveInfo(void)
|
||||
{
|
||||
// Clear userSaveInfo before JIC
|
||||
for(auto &u : s_UserVector)
|
||||
for (auto &u : s_UserVector)
|
||||
{
|
||||
u.second.clearUserSaveInfo();
|
||||
}
|
||||
@@ -138,18 +139,18 @@ void data::loadUserSaveInfo(void)
|
||||
int64_t entries = 0;
|
||||
|
||||
Result openSaveInfoReader = fsOpenSaveDataInfoReader(&saveDataInfoReader, FsSaveDataSpaceId_All);
|
||||
if(R_FAILED(openSaveInfoReader))
|
||||
if (R_FAILED(openSaveInfoReader))
|
||||
{
|
||||
logger::log("fsOpenSaveDataInfoReader failed: 0x%X", openSaveInfoReader);
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop and load all saves and push them to users
|
||||
while(R_SUCCEEDED(fsSaveDataInfoReaderRead(&saveDataInfoReader, &saveDataInfo, 1, &entries)) && entries > 0)
|
||||
while (R_SUCCEEDED(fsSaveDataInfoReaderRead(&saveDataInfoReader, &saveDataInfo, 1, &entries)) && entries > 0)
|
||||
{
|
||||
// Title id depends on save type
|
||||
uint64_t titleID = 0;
|
||||
if(saveDataInfo.save_data_type == FsSaveDataType_System || saveDataInfo.save_data_type == FsSaveDataType_SystemBcat)
|
||||
if (saveDataInfo.save_data_type == FsSaveDataType_System || saveDataInfo.save_data_type == FsSaveDataType_SystemBcat)
|
||||
{
|
||||
titleID = saveDataInfo.system_save_data_id;
|
||||
}
|
||||
@@ -159,14 +160,14 @@ void data::loadUserSaveInfo(void)
|
||||
}
|
||||
|
||||
// Just in case it doesn't have and entry
|
||||
if(titleIsLoadedInMap(titleID) == false)
|
||||
if (titleIsLoadedInMap(titleID) == false)
|
||||
{
|
||||
logger::log("Save found for for title not in records: 0x%016lX", titleID);
|
||||
s_TitleMap.emplace(std::make_pair(titleID, data::titleInfo(titleID)));
|
||||
}
|
||||
|
||||
// Just skip this stuff. Comes after above JIC we need to load something.
|
||||
if(isAccountSystemSave(saveDataInfo) && config::getByKey(CONFIG_LIST_ACCOUNT_SYSTEM_SAVES) == false)
|
||||
if (isAccountSystemSave(saveDataInfo) && config::getByKey(CONFIG_LIST_ACCOUNT_SYSTEM_SAVES) == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -189,7 +190,7 @@ void data::loadUserSaveInfo(void)
|
||||
case FsSaveDataType_Temporary:
|
||||
{
|
||||
// This is special for compatibility just in case.
|
||||
if(userExistsInVector(TEMPORARY_SAVE_USER_ID) == false)
|
||||
if (userExistsInVector(TEMPORARY_SAVE_USER_ID) == false)
|
||||
{
|
||||
createAddSystemUser(TEMPORARY_SAVE_USER_ID);
|
||||
}
|
||||
@@ -206,7 +207,7 @@ void data::loadUserSaveInfo(void)
|
||||
// I've never seen this used before...
|
||||
case FsSaveDataType_SystemBcat:
|
||||
{
|
||||
if(userExistsInVector(SYSTEM_BCAT_USER_ID) == false)
|
||||
if (userExistsInVector(SYSTEM_BCAT_USER_ID) == false)
|
||||
{
|
||||
createAddSystemUser(SYSTEM_BCAT_USER_ID);
|
||||
}
|
||||
@@ -217,7 +218,7 @@ void data::loadUserSaveInfo(void)
|
||||
|
||||
// Play stats
|
||||
PdmPlayStatistics playStatistics;
|
||||
if(saveDataInfo.save_data_type == FsSaveDataType_Account || saveDataInfo.save_data_type == FsSaveDataType_Device)
|
||||
if (saveDataInfo.save_data_type == FsSaveDataType_Account || saveDataInfo.save_data_type == FsSaveDataType_Device)
|
||||
{
|
||||
pdmqryQueryPlayStatisticsByApplicationIdAndUserAccountId(titleID, saveDataInfo.uid, false, &playStatistics);
|
||||
}
|
||||
@@ -228,7 +229,7 @@ void data::loadUserSaveInfo(void)
|
||||
|
||||
// Finally push it to user
|
||||
data::user *targetUser = data::getUserByAccountID(accountUIDToU128(saveDataInfo.uid));
|
||||
if(targetUser != NULL)
|
||||
if (targetUser != NULL)
|
||||
{
|
||||
targetUser->addNewUserSaveInfo(titleID, saveDataInfo, playStatistics);
|
||||
}
|
||||
@@ -240,7 +241,7 @@ void data::loadUserSaveInfo(void)
|
||||
|
||||
void data::sortUserSaveInfo(void)
|
||||
{
|
||||
for(userIDPair &userPair : s_UserVector)
|
||||
for (userIDPair &userPair : s_UserVector)
|
||||
{
|
||||
userPair.second.sortUserSaveInfo();
|
||||
}
|
||||
@@ -253,8 +254,9 @@ int data::getTotalUsers(void)
|
||||
|
||||
data::user *data::getUserByAccountID(u128 accountID)
|
||||
{
|
||||
auto userPosition = std::find_if(s_UserVector.begin(), s_UserVector.end(), [accountID](const userIDPair &u){ return u.first == accountID; });
|
||||
if(userPosition != s_UserVector.end())
|
||||
auto userPosition = std::find_if(s_UserVector.begin(), s_UserVector.end(), [accountID](const userIDPair &u)
|
||||
{ return u.first == accountID; });
|
||||
if (userPosition != s_UserVector.end())
|
||||
{
|
||||
return &userPosition->second;
|
||||
}
|
||||
@@ -263,7 +265,7 @@ data::user *data::getUserByAccountID(u128 accountID)
|
||||
|
||||
data::user *data::getUserAtPosition(int position)
|
||||
{
|
||||
if(position >= 0 && position < static_cast<int>(s_UserVector.size()))
|
||||
if (position >= 0 && position < static_cast<int>(s_UserVector.size()))
|
||||
{
|
||||
return &s_UserVector.at(position).second;
|
||||
}
|
||||
@@ -282,7 +284,7 @@ int data::getTotalTitleCount(void)
|
||||
|
||||
data::titleInfo *data::getTitleInfoByTitleID(uint64_t titleID)
|
||||
{
|
||||
if(s_TitleMap.find(titleID) != s_TitleMap.end())
|
||||
if (s_TitleMap.find(titleID) != s_TitleMap.end())
|
||||
{
|
||||
return &s_TitleMap.at(titleID);
|
||||
}
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
#include "stringUtil.hpp"
|
||||
#include "log.hpp"
|
||||
|
||||
data::titleInfo::titleInfo(uint64_t titleID) :
|
||||
m_TitleID(titleID)
|
||||
data::titleInfo::titleInfo(uint64_t titleID) : m_TitleID(titleID)
|
||||
{
|
||||
// This is the size of the control data. Used for getting icon size
|
||||
uint64_t nsAppControlSize = 0;
|
||||
@@ -22,7 +21,7 @@ m_TitleID(titleID)
|
||||
std::string titleIDHexString = stringUtil::getFormattedString("%016lX", m_TitleID);
|
||||
|
||||
Result nsAppControlRes = nsGetApplicationControlData(NsApplicationControlSource_Storage, m_TitleID, nsAppConrolData.get(), sizeof(NsApplicationControlData), &nsAppControlSize);
|
||||
if(R_SUCCEEDED(nsAppControlRes) && !(nsAppControlSize < sizeof(nsAppConrolData->nacp)))
|
||||
if (R_SUCCEEDED(nsAppControlRes) && !(nsAppControlSize < sizeof(nsAppConrolData->nacp)))
|
||||
{
|
||||
appIconSize = nsAppControlSize - sizeof(nsAppConrolData->nacp);
|
||||
|
||||
@@ -49,7 +48,7 @@ std::string data::titleInfo::getTitle(void)
|
||||
NacpLanguageEntry *entry;
|
||||
nacpGetLanguageEntry(&m_Nacp, &entry);
|
||||
// If it fails, return title ID as hex string
|
||||
if(entry == NULL)
|
||||
if (entry == NULL)
|
||||
{
|
||||
return stringUtil::getFormattedString("%016lX", m_TitleID);
|
||||
}
|
||||
@@ -62,7 +61,7 @@ std::string data::titleInfo::getPathSafeTitle(void)
|
||||
// Try to get a pathSafeTitle
|
||||
std::string pathSafeTitle = stringUtil::getPathSafeString(data::titleInfo::getTitle());
|
||||
// If that fails, it will be empty. Just return ID hex string
|
||||
if(pathSafeTitle.empty())
|
||||
if (pathSafeTitle.empty())
|
||||
{
|
||||
return stringUtil::getFormattedString("%016lX", m_TitleID);
|
||||
}
|
||||
@@ -77,7 +76,7 @@ graphics::sdlTexture data::titleInfo::getIcon(void)
|
||||
int64_t data::titleInfo::getSaveDataSize(FsSaveDataType saveType)
|
||||
{
|
||||
int64_t saveDataSize = 0;
|
||||
switch(saveType)
|
||||
switch (saveType)
|
||||
{
|
||||
case FsSaveDataType_Account:
|
||||
{
|
||||
@@ -110,7 +109,7 @@ int64_t data::titleInfo::getSaveDataSizeMax(FsSaveDataType saveType)
|
||||
int64_t saveSize = 0;
|
||||
int64_t saveSizeMax = 0;
|
||||
|
||||
switch(saveType)
|
||||
switch (saveType)
|
||||
{
|
||||
case FsSaveDataType_Account:
|
||||
{
|
||||
@@ -148,7 +147,7 @@ int64_t data::titleInfo::getJournalSize(FsSaveDataType saveType)
|
||||
int64_t journalSize = 0;
|
||||
|
||||
// Different save types have different journal limits
|
||||
switch(saveType)
|
||||
switch (saveType)
|
||||
{
|
||||
case FsSaveDataType_Account:
|
||||
{
|
||||
|
||||
@@ -30,39 +30,39 @@ bool compareTitles(const data::userSaveInfo &a, const data::userSaveInfo &b)
|
||||
data::titleInfo *titleInfoB = data::getTitleInfoByTitleID(b.getTitleID());
|
||||
switch (config::getByKey(CONFIG_TITLE_SORT_TYPE))
|
||||
{
|
||||
case config::SORT_TYPE_ALPHA:
|
||||
{
|
||||
uint32_t codepointA, codepointB;
|
||||
std::string titleA = titleInfoA->getTitle();
|
||||
std::string titleB = titleInfoB->getTitle();
|
||||
int titleLength = titleA.length();
|
||||
for (int i = 0, j = 0; i < titleLength;)
|
||||
case config::SORT_TYPE_ALPHA:
|
||||
{
|
||||
ssize_t aCount = decode_utf8(&codepointA, reinterpret_cast<const uint8_t *>(&titleA.c_str()[i]));
|
||||
ssize_t bCount = decode_utf8(&codepointB, reinterpret_cast<const uint8_t *>(&titleB.c_str()[j]));
|
||||
codepointA = std::tolower(codepointA);
|
||||
codepointB = std::tolower(codepointB);
|
||||
if (codepointA != codepointB)
|
||||
uint32_t codepointA, codepointB;
|
||||
std::string titleA = titleInfoA->getTitle();
|
||||
std::string titleB = titleInfoB->getTitle();
|
||||
int titleLength = titleA.length();
|
||||
for (int i = 0, j = 0; i < titleLength;)
|
||||
{
|
||||
return codepointA < codepointB;
|
||||
ssize_t aCount = decode_utf8(&codepointA, reinterpret_cast<const uint8_t *>(&titleA.c_str()[i]));
|
||||
ssize_t bCount = decode_utf8(&codepointB, reinterpret_cast<const uint8_t *>(&titleB.c_str()[j]));
|
||||
codepointA = std::tolower(codepointA);
|
||||
codepointB = std::tolower(codepointB);
|
||||
if (codepointA != codepointB)
|
||||
{
|
||||
return codepointA < codepointB;
|
||||
}
|
||||
i += aCount;
|
||||
j += bCount;
|
||||
}
|
||||
i += aCount;
|
||||
j += bCount;
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case config::SORT_TYPE_MOST_PLAYED:
|
||||
{
|
||||
return a.getPlayStatistics().playtime > b.getPlayStatistics().playtime;
|
||||
}
|
||||
break;
|
||||
case config::SORT_TYPE_MOST_PLAYED:
|
||||
{
|
||||
return a.getPlayStatistics().playtime > b.getPlayStatistics().playtime;
|
||||
}
|
||||
break;
|
||||
|
||||
case config::SORT_TYPE_LAST_PLAYED:
|
||||
{
|
||||
return a.getPlayStatistics().last_timestamp_user > b.getPlayStatistics().last_timestamp_user;
|
||||
}
|
||||
break;
|
||||
case config::SORT_TYPE_LAST_PLAYED:
|
||||
{
|
||||
return a.getPlayStatistics().last_timestamp_user > b.getPlayStatistics().last_timestamp_user;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -115,10 +115,10 @@ data::user::user(AccountUid accountID, data::userType userType) : m_AccountID(ac
|
||||
}
|
||||
|
||||
data::user::user(AccountUid accountID, const std::string &username, const std::string &pathSafeUsername, data::userType userType) : m_AccountID(accountID),
|
||||
m_Username(username),
|
||||
m_PathSafeUsername(pathSafeUsername),
|
||||
m_Icon(graphics::createIcon(m_Username, m_Username, 42)),
|
||||
m_UserType(userType) {}
|
||||
m_Username(username),
|
||||
m_PathSafeUsername(pathSafeUsername),
|
||||
m_Icon(graphics::createIcon(m_Username, m_Username, 42)),
|
||||
m_UserType(userType) {}
|
||||
|
||||
void data::user::addNewUserSaveInfo(uint64_t titleID, const FsSaveDataInfo &saveInfo, const PdmPlayStatistics &playStats)
|
||||
{
|
||||
@@ -160,7 +160,7 @@ graphics::sdlTexture data::user::getUserIcon(void) const
|
||||
return m_Icon;
|
||||
}
|
||||
|
||||
data::userType data::user::getUserType(void) const
|
||||
data::userType data::user::getUserType(void) const
|
||||
{
|
||||
return m_UserType;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#include "data/userSaveInfo.hpp"
|
||||
|
||||
data::userSaveInfo::userSaveInfo(uint64_t titleID, const FsSaveDataInfo &saveInfo, const PdmPlayStatistics &playStats) :
|
||||
m_TitleID(titleID),
|
||||
m_SaveDataInfo(saveInfo),
|
||||
m_PlayStats(playStats) { }
|
||||
data::userSaveInfo::userSaveInfo(uint64_t titleID, const FsSaveDataInfo &saveInfo, const PdmPlayStatistics &playStats) : m_TitleID(titleID),
|
||||
m_SaveDataInfo(saveInfo),
|
||||
m_PlayStats(playStats) {}
|
||||
|
||||
uint64_t data::userSaveInfo::getTitleID(void) const
|
||||
{
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
#include "stringUtil.hpp"
|
||||
#include "log.hpp"
|
||||
|
||||
fs::directoryListing::directoryListing(const std::string &directoryPath) :
|
||||
m_DirectoryPath(directoryPath)
|
||||
fs::directoryListing::directoryListing(const std::string &directoryPath) : m_DirectoryPath(directoryPath)
|
||||
{
|
||||
loadListing();
|
||||
}
|
||||
@@ -19,7 +18,7 @@ void fs::directoryListing::loadListing(void)
|
||||
// Error code
|
||||
std::error_code errorCode;
|
||||
|
||||
for(const std::filesystem::directory_entry &entry : std::filesystem::directory_iterator(m_DirectoryPath, errorCode))
|
||||
for (const std::filesystem::directory_entry &entry : std::filesystem::directory_iterator(m_DirectoryPath, errorCode))
|
||||
{
|
||||
m_DirectoryList.push_back(entry);
|
||||
}
|
||||
|
||||
@@ -142,8 +142,8 @@ bool fs::createSaveDataFileSystem(FsSaveDataType saveType, uint64_t titleID, Acc
|
||||
|
||||
FsSaveDataMetaInfo saveMetaInfo =
|
||||
{
|
||||
.size = saveType == FsSaveDataType_Bcat ? 0 : 0x40060,
|
||||
.type = saveType == FsSaveDataType_Bcat ? 0 : FsSaveDataMetaType_Thumbnail
|
||||
.size = saveType == FsSaveDataType_Bcat ? static_cast<uint32_t>(0) : static_cast<uint32_t>(0x40060),
|
||||
.type = saveType == FsSaveDataType_Bcat ? static_cast<uint8_t>(0) : static_cast<uint8_t>(FsSaveDataMetaType_Thumbnail)
|
||||
};
|
||||
|
||||
Result createSaveResult = fsCreateSaveDataFileSystem(&saveDataAttributes, &saveCreationInfo, &saveMetaInfo);
|
||||
|
||||
@@ -31,15 +31,12 @@ namespace
|
||||
FT_Face s_FTFaces[6];
|
||||
//Number of fonts loaded
|
||||
int s_TotalFonts;
|
||||
// Color used for rendering. Added to prevent duplicating code.
|
||||
uint32_t s_RenderingColor;
|
||||
// This is the glyph texture cache map
|
||||
std::map<std::pair<uint32_t, int>, glyphData> s_GlyphMap;
|
||||
// How large the word buffer is for line wrapping.
|
||||
const int WORD_WRAP_BUFFER_LENGTH = 0x1000;
|
||||
// Masks for converting glyphs to sdl surface
|
||||
const uint32_t s_RedMask = 0xFF000000;
|
||||
const uint32_t s_GreenMask = 0x00FF0000;
|
||||
const uint32_t s_BlueMask = 0x0000FF00;
|
||||
const uint32_t s_AlphaMask = 0x000000FF;
|
||||
// Codepoints to break lines at
|
||||
const std::array<uint32_t, 7> s_BreakPoints = {L' ', L' ', L'/', L'_', L'-', L'。', L'、'};
|
||||
}
|
||||
@@ -99,7 +96,7 @@ static glyphData *getGlyph(uint32_t codepoint, int fontSize)
|
||||
}
|
||||
|
||||
// Create Temporary SDL_Surface to convert to texture
|
||||
SDL_Surface *tempGlyphSurface = SDL_CreateRGBSurfaceFrom(glyphPixelData.data(), bitmap.width, bitmap.rows, 32, sizeof(uint32_t) * bitmap.width, s_RedMask, s_GreenMask, s_BlueMask, s_AlphaMask);
|
||||
SDL_Surface *tempGlyphSurface = SDL_CreateRGBSurfaceFrom(glyphPixelData.data(), bitmap.width, bitmap.rows, 32, sizeof(uint32_t) * bitmap.width, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
|
||||
// Texture manager needs name for texture.
|
||||
std::string glyphName = std::to_string(codepoint) + "_" + std::to_string(fontSize);
|
||||
|
||||
@@ -153,32 +150,33 @@ static size_t findNextBreakPoint(const char *str)
|
||||
}
|
||||
|
||||
//Processes characters set to change text colors. Returns true if character is special
|
||||
static bool processSpecialCharacters(uint32_t codepoint, uint32_t originalColor, uint32_t &color)
|
||||
static bool processSpecialCharacters(uint32_t codepoint, uint32_t originalColor)
|
||||
{
|
||||
// This is set to true and on default, set to false.
|
||||
bool isSpecial = true;
|
||||
switch (codepoint)
|
||||
{
|
||||
case L'#':
|
||||
{
|
||||
color = color == COLOR_BLUE ? originalColor : COLOR_BLUE;
|
||||
s_RenderingColor = s_RenderingColor == originalColor ? COLOR_BLUE : originalColor;
|
||||
}
|
||||
break;
|
||||
|
||||
case L'*':
|
||||
{
|
||||
color = color == COLOR_RED ? originalColor : COLOR_RED;
|
||||
s_RenderingColor = s_RenderingColor == originalColor ? COLOR_RED : originalColor;
|
||||
}
|
||||
break;
|
||||
|
||||
case L'<':
|
||||
{
|
||||
color = color == COLOR_YELLOW ? originalColor : COLOR_YELLOW;
|
||||
s_RenderingColor = s_RenderingColor == originalColor ? COLOR_YELLOW : originalColor;
|
||||
}
|
||||
break;
|
||||
|
||||
case L'>':
|
||||
{
|
||||
color = color == COLOR_GREEN ? originalColor : COLOR_GREEN;
|
||||
s_RenderingColor = s_RenderingColor == originalColor ? COLOR_GREEN : originalColor;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -188,6 +186,7 @@ static bool processSpecialCharacters(uint32_t codepoint, uint32_t originalColor,
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return isSpecial;
|
||||
}
|
||||
|
||||
@@ -249,7 +248,7 @@ void graphics::systemFont::renderText(const std::string &text, SDL_Texture *targ
|
||||
int workingY = y;
|
||||
|
||||
//Color we're working with
|
||||
uint32_t workingColor = color;
|
||||
s_RenderingColor = color;
|
||||
|
||||
//Current character/codepoint
|
||||
uint32_t codepoint = 0;
|
||||
@@ -272,7 +271,7 @@ void graphics::systemFont::renderText(const std::string &text, SDL_Texture *targ
|
||||
}
|
||||
|
||||
i += unitCount;
|
||||
if(processSpecialCharacters(codepoint, color, workingColor))
|
||||
if(processSpecialCharacters(codepoint, color))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -288,7 +287,7 @@ void graphics::systemFont::renderText(const std::string &text, SDL_Texture *targ
|
||||
if(glyph)
|
||||
{
|
||||
// Set color just to be sure
|
||||
SDL_SetTextureColorMod(glyph->glyph.get(), getRed(workingColor), getGreen(workingColor), getBlue(workingColor));
|
||||
SDL_SetTextureColorMod(glyph->glyph.get(), getRed(s_RenderingColor), getGreen(s_RenderingColor), getBlue(s_RenderingColor));
|
||||
// Render glyph texture
|
||||
graphics::textureRender(glyph->glyph.get(), target, workingX + glyph->left, workingY + (fontSize - glyph->top));
|
||||
// X update
|
||||
@@ -303,6 +302,7 @@ void graphics::systemFont::renderTextWrap(const std::string &text, SDL_Texture *
|
||||
int workingX = x;
|
||||
int workingY = y;
|
||||
int stringLength = text.length();
|
||||
|
||||
// This buffer is used to calculate if the next word should create a line break
|
||||
std::array<char, WORD_WRAP_BUFFER_LENGTH> wordBuffer = { 0 };
|
||||
|
||||
@@ -338,7 +338,6 @@ int graphics::systemFont::getTextWidth(const std::string &text, int fontSize)
|
||||
{
|
||||
int textWidth = 0;
|
||||
uint32_t codepoint = 0;
|
||||
uint32_t colorMod = 0; // This is just needed so we can properly count and skip special chars
|
||||
ssize_t unitCount = 0;
|
||||
|
||||
resizeFont(fontSize);
|
||||
@@ -352,7 +351,7 @@ int graphics::systemFont::getTextWidth(const std::string &text, int fontSize)
|
||||
}
|
||||
|
||||
i += unitCount;
|
||||
if(processSpecialCharacters(codepoint, COLOR_WHITE, colorMod))
|
||||
if(processSpecialCharacters(codepoint, COLOR_WHITE))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -11,22 +11,27 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
int menuID = 0;
|
||||
// This is how much extra vertical space is needed for bounding/selection box
|
||||
const int BOUNDING_EXTRA_HEIGHT = 32;
|
||||
}
|
||||
|
||||
ui::menu::menu(int x, int y, int rectWidth, int fontSize, int maxScroll) :
|
||||
m_X(x),
|
||||
m_Y(y),
|
||||
m_OriginalY(y),
|
||||
m_FontSize(fontSize),
|
||||
m_RectWidth(rectWidth),
|
||||
m_RectHeight(m_FontSize + BOUNDING_EXTRA_HEIGHT),
|
||||
m_Selected(0),
|
||||
m_MaxScroll(maxScroll) { }
|
||||
ui::menu::menu(int x, int y, int rectWidth, int fontSize, int maxScroll) : m_X(x),
|
||||
m_Y(y),
|
||||
m_OriginalY(y),
|
||||
m_FontSize(fontSize),
|
||||
m_RectWidth(rectWidth),
|
||||
m_RectHeight(m_FontSize + BOUNDING_EXTRA_HEIGHT),
|
||||
m_Selected(0),
|
||||
m_MaxScroll(maxScroll)
|
||||
{
|
||||
// String for rendertarget
|
||||
std::string menuTargetName = "menuTarget_" + std::to_string(menuID++);
|
||||
// This is for rendering the options to. So text can't be rendered outside bounding box.
|
||||
m_OptionRenderTarget = graphics::textureManager::createTexture(menuTargetName, m_RectWidth, m_RectHeight, SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET);
|
||||
}
|
||||
|
||||
ui::menu::menu(int x, int y, int rectWidth, int fontSize, int maxScroll, const std::string *menuOptions, int menuOptCount) :
|
||||
menu(x, y, rectWidth, fontSize, maxScroll)
|
||||
ui::menu::menu(int x, int y, int rectWidth, int fontSize, int maxScroll, const std::string *menuOptions, int menuOptCount) : menu(x, y, rectWidth, fontSize, maxScroll)
|
||||
{
|
||||
for (int i = 0; i < menuOptCount; i++)
|
||||
{
|
||||
@@ -53,6 +58,14 @@ void ui::menu::update(void)
|
||||
{
|
||||
m_Selected = 0;
|
||||
}
|
||||
else if (sys::input::buttonDown(HidNpadButton_AnyLeft) && (m_Selected -= m_MaxScroll) < 0)
|
||||
{
|
||||
m_Selected = 0;
|
||||
}
|
||||
else if (sys::input::buttonDown(HidNpadButton_AnyRight) && (m_Selected += m_MaxScroll) > menuSize)
|
||||
{
|
||||
m_Selected = menuSize;
|
||||
}
|
||||
|
||||
// Calculate if scrolling needs to happen
|
||||
if (m_Selected <= m_MaxScroll)
|
||||
@@ -73,7 +86,7 @@ void ui::menu::update(void)
|
||||
{
|
||||
double addToY = static_cast<double>(static_cast<double>(m_TargetY) - static_cast<double>(m_Y)) / config::getAnimationScaling();
|
||||
m_Y += std::ceil(addToY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ui::menu::render(SDL_Texture *target)
|
||||
@@ -89,11 +102,17 @@ void ui::menu::render(SDL_Texture *target)
|
||||
SDL_QueryTexture(target, NULL, NULL, NULL, &targetHeight);
|
||||
for (int i = 0; i < (int)m_MenuOptions.size(); i++)
|
||||
{
|
||||
// Clear option target
|
||||
graphics::textureClear(m_OptionRenderTarget.get(), COLOR_TRANSPARENT);
|
||||
// Render option to it.
|
||||
graphics::systemFont::renderText(m_MenuOptions.at(i), m_OptionRenderTarget.get(), 16, (m_RectHeight / 2) - (m_FontSize /2), m_FontSize, COLOR_WHITE);
|
||||
// Render option target to target
|
||||
graphics::textureRender(m_OptionRenderTarget.get(), target, m_X, m_Y + (i * m_RectHeight));
|
||||
// If it's the selected option, render the selection box over top.
|
||||
if (m_Selected == i)
|
||||
{
|
||||
ui::renderSelectionBox(target, m_X - 4, (m_Y + (i * m_RectHeight)) - 4, m_RectWidth, m_RectHeight, m_ColorMod);
|
||||
ui::renderSelectionBox(target, m_X, (m_Y + (i * m_RectHeight)), m_RectWidth, m_RectHeight, m_ColorMod);
|
||||
}
|
||||
graphics::systemFont::renderText(m_MenuOptions.at(i), target, m_X + 8, m_Y + (i * m_RectHeight) + (m_RectHeight / 2 - m_FontSize / 2), m_FontSize, COLOR_WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +123,7 @@ void ui::menu::addOption(const std::string &newOption)
|
||||
|
||||
void ui::menu::updateColorPulse(void)
|
||||
{
|
||||
if (m_ColorShift && (m_ColorMod += 6) >= 0x72)
|
||||
if (m_ColorShift && (m_ColorMod += 6) >= 0x72)
|
||||
{
|
||||
m_ColorShift = false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user