Merge pull request #789 from erorcun/master

rewrite CFO + postfx/pipeline options
This commit is contained in:
erorcun
2020-11-03 01:43:53 +03:00
committed by GitHub
9 changed files with 1175 additions and 887 deletions

View File

@ -1,20 +1,14 @@
#include "common.h"
#ifdef CUSTOM_FRONTEND_OPTIONS
#include "frontendoption.h"
#include "Frontend.h"
#include "Text.h"
int numCustomFrontendOptions = 0;
FrontendOption *customFrontendOptions;
int numCustomFrontendScreens = 0;
FrontendScreen* customFrontendScreens;
int numFrontendOptionReplacements = 0;
CMenuScreen::CMenuEntry* frontendOptionReplacements;
int lastOgScreen = MENUPAGES; // means no new pages
int numCustomFrontendOptions = 0;
int numCustomFrontendScreens = 0;
int optionCursor = -2;
int currentMenu;
bool optionOverwrite = false;
@ -32,8 +26,7 @@ void GoBack(bool fadeIn)
{
int screen = !FrontEndMenuManager.m_bGameNotLoaded ?
aScreens[FrontEndMenuManager.m_nCurrScreen].m_PreviousPage[1] : aScreens[FrontEndMenuManager.m_nCurrScreen].m_PreviousPage[0];
int option = !FrontEndMenuManager.m_bGameNotLoaded ?
aScreens[FrontEndMenuManager.m_nCurrScreen].m_ParentEntry[1] : aScreens[FrontEndMenuManager.m_nCurrScreen].m_ParentEntry[0];
int option = FrontEndMenuManager.GetPreviousPageOption();
FrontEndMenuManager.ThingsToDoBeforeGoingBack();
@ -58,7 +51,7 @@ GetLastMenuScreen()
{
int8 page = -1;
for (int i = 0; i < MENUPAGES; i++) {
if (strcmp(aScreens[i].m_ScreenName, "") == 0 && aScreens[i].unk == 0)
if (strcmp(aScreens[i].m_ScreenName, "") == 0 && aScreens[i].m_PreviousPage[0] == MENUPAGE_NONE)
break;
++page;
@ -66,89 +59,23 @@ GetLastMenuScreen()
return page;
}
// Used before populating options, but effective in InitialiseChangedLanguageSettings and debugmenu
void
RemoveCustomFrontendOptions()
{
if (numCustomFrontendOptions != 0) {
for (int i = 0; i < MENUPAGES; i++) {
for (int j = 0; j < NUM_MENUROWS; j++) {
if (aScreens[i].m_aEntries[j].m_SaveSlot == SAVESLOT_CFO) {
int ogOptionId = customFrontendOptions[aScreens[i].m_aEntries[j].m_TargetMenu].ogOptionId;
if (customFrontendOptions[aScreens[i].m_aEntries[j].m_TargetMenu].type == FEOPTION_SELECT)
free(customFrontendOptions[aScreens[i].m_aEntries[j].m_TargetMenu].rightTexts);
if (ogOptionId == -1) {
int k;
for (k = j; k < NUM_MENUROWS - 1; k++) {
memcpy(&aScreens[i].m_aEntries[k], &aScreens[i].m_aEntries[k + 1], sizeof(CMenuScreen::CMenuEntry));
}
aScreens[i].m_aEntries[k].m_Action = MENUACTION_NOTHING;
aScreens[i].m_aEntries[k].m_SaveSlot = SAVESLOT_NONE;
aScreens[i].m_aEntries[k].m_EntryName[0] = '\0';
j--;
} else {
memcpy(&aScreens[i].m_aEntries[j], &frontendOptionReplacements[ogOptionId], sizeof(CMenuScreen::CMenuEntry));
}
}
}
}
free(customFrontendOptions);
numCustomFrontendOptions = 0;
if (numFrontendOptionReplacements != 0) {
free(frontendOptionReplacements);
numFrontendOptionReplacements = 0;
}
}
if (numCustomFrontendScreens == 0)
return;
for (int i = 0; i < MENUPAGES; i++) {
if (i > lastOgScreen) {
aScreens[i].m_ScreenName[0] = '\0';
aScreens[i].unk = 0;
}
}
free(customFrontendScreens);
numCustomFrontendScreens = 0;
lastOgScreen = MENUPAGES;
}
int8 RegisterNewScreen(const char *name, int prevPage)
int8 RegisterNewScreen(const char *name, int prevPage, ReturnPrevPageFunc returnPrevPageFunc)
{
if (lastOgScreen == MENUPAGES)
lastOgScreen = GetLastMenuScreen();
numCustomFrontendScreens++;
if (numCustomFrontendScreens == 1)
customFrontendScreens = (FrontendScreen*)malloc(5 * sizeof(FrontendScreen));
else if (numCustomFrontendScreens % 5 == 1)
customFrontendScreens = (FrontendScreen*)realloc(customFrontendScreens, (numCustomFrontendScreens + 4) * sizeof(FrontendScreen));
assert(customFrontendScreens != nil && "Custom frontend screens can't be allocated");
int id = lastOgScreen + numCustomFrontendScreens;
assert(id < MENUPAGES && "No room for new custom frontend screens! Increase MENUPAGES");
strncpy(aScreens[id].m_ScreenName, name, 8);
aScreens[id].m_PreviousPage[0] = aScreens[id].m_PreviousPage[1] = prevPage;
aScreens[id].unk = 1;
aScreens[id].returnPrevPageFunc = returnPrevPageFunc;
return id;
}
int8 RegisterNewOption()
{
numCustomFrontendOptions++;
if (numCustomFrontendOptions == 1)
customFrontendOptions = (FrontendOption*)malloc(5 * sizeof(FrontendOption));
else if (numCustomFrontendOptions % 5 == 1)
customFrontendOptions = (FrontendOption*)realloc(customFrontendOptions, (numCustomFrontendOptions + 4) * sizeof(FrontendOption));
assert(customFrontendOptions != nil && "Custom frontend options can't be allocated");
uint8 numOptions = GetNumberOfMenuOptions(currentMenu);
uint8 curIdx;
if (optionCursor < 0) {
@ -159,30 +86,11 @@ int8 RegisterNewOption()
if (!optionOverwrite) {
if (aScreens[currentMenu].m_aEntries[curIdx].m_Action != MENUACTION_NOTHING) {
for (int i = numOptions - 1; i >= curIdx; i--) {
memcpy(&aScreens[currentMenu].m_aEntries[i + 1], &aScreens[currentMenu].m_aEntries[i], sizeof(CMenuScreen::CMenuEntry));
memcpy(&aScreens[currentMenu].m_aEntries[i + 1], &aScreens[currentMenu].m_aEntries[i], sizeof(CMenuScreenCustom::CMenuEntry));
}
}
}
optionCursor++;
if (optionOverwrite) {
numFrontendOptionReplacements++;
if (numFrontendOptionReplacements == 1)
frontendOptionReplacements = (CMenuScreen::CMenuEntry*)malloc(5 * sizeof(CMenuScreen::CMenuEntry));
else if (numFrontendOptionReplacements % 5 == 1)
frontendOptionReplacements = (CMenuScreen::CMenuEntry*)realloc(frontendOptionReplacements, (numFrontendOptionReplacements + 4) * sizeof(CMenuScreen::CMenuEntry));
memcpy(&frontendOptionReplacements[numFrontendOptionReplacements - 1], &aScreens[currentMenu].m_aEntries[curIdx], sizeof(CMenuScreen::CMenuEntry));
customFrontendOptions[numCustomFrontendOptions - 1].ogOptionId = numFrontendOptionReplacements - 1;
} else {
customFrontendOptions[numCustomFrontendOptions - 1].ogOptionId = -1;
}
customFrontendOptions[numCustomFrontendOptions - 1].screen = currentMenu;
aScreens[currentMenu].m_aEntries[curIdx].m_Action = MENUACTION_TRIGGERFUNC;
aScreens[currentMenu].m_aEntries[curIdx].m_SaveSlot = SAVESLOT_CFO;
aScreens[currentMenu].m_aEntries[curIdx].m_TargetMenu = numCustomFrontendOptions - 1;
aScreens[currentMenu].m_aEntries[curIdx].m_EntryName[0] = 1; // just something to fool it
return curIdx;
}
@ -193,110 +101,78 @@ void FrontendOptionSetCursor(int screen, int8 option, bool overwrite)
optionOverwrite = overwrite;
}
void FrontendOptionAddBuiltinAction(const wchar* leftText, int action, ButtonPressFunc buttonPressFunc, ReturnPrevPageFunc returnPrevPageFunc) {
void FrontendOptionAddBuiltinAction(const char* gxtKey, int action, int targetMenu, int saveSlot) {
int8 screenOptionOrder = RegisterNewOption();
FrontendOption& option = customFrontendOptions[numCustomFrontendOptions - 1];
CMenuScreenCustom::CMenuEntry &option = aScreens[currentMenu].m_aEntries[screenOptionOrder];
// To fool the Frontend, we will still display the text passed via first param.
// We can't use custom text on those :shrug:
switch (action) {
case MENUACTION_SCREENRES:
strcpy(aScreens[currentMenu].m_aEntries[screenOptionOrder].m_EntryName, "FED_RES");
strcpy(option.m_EntryName, "FED_RES");
break;
case MENUACTION_AUDIOHW:
strcpy(aScreens[currentMenu].m_aEntries[screenOptionOrder].m_EntryName, "FEA_3DH");
strcpy(option.m_EntryName, "FEA_3DH");
break;
default:
strncpy(option.m_EntryName, gxtKey, 8);
break;
}
aScreens[currentMenu].m_aEntries[screenOptionOrder].m_Action = action;
option.type = FEOPTION_BUILTIN_ACTION;
option.buttonPressFunc = buttonPressFunc;
TextCopy(option.leftText, leftText);
option.screenOptionOrder = screenOptionOrder;
option.returnPrevPageFunc = returnPrevPageFunc;
option.save = nil;
option.m_Action = action;
option.m_SaveSlot = saveSlot;
option.m_TargetMenu = targetMenu;
}
void FrontendOptionAddSelect(const wchar* leftText, const wchar** rightTexts, int8 numRightTexts, int8 *var, bool onlyApplyOnEnter, ChangeFunc changeFunc, ReturnPrevPageFunc returnPrevPageFunc, const char* saveName)
void FrontendOptionAddSelect(const char* gxtKey, const char** rightTexts, int8 numRightTexts, int8 *var, bool onlyApplyOnEnter, ChangeFunc changeFunc, const char* saveName)
{
int8 screenOptionOrder = RegisterNewOption();
CMenuScreenCustom::CMenuEntry &option = aScreens[currentMenu].m_aEntries[screenOptionOrder];
option.m_Action = MENUACTION_CFO_SELECT;
strncpy(option.m_EntryName, gxtKey, 8);
option.m_CFOSelect = new CCFOSelect();
option.m_CFOSelect->rightTexts = (char**)malloc(numRightTexts * sizeof(char*));
memcpy(option.m_CFOSelect->rightTexts, rightTexts, numRightTexts * sizeof(char*));
option.m_CFOSelect->numRightTexts = numRightTexts;
option.m_CFOSelect->value = var;
if (var) {
option.m_CFOSelect->displayedValue = *var;
option.m_CFOSelect->lastSavedValue = *var;
}
option.m_CFOSelect->save = saveName;
option.m_CFOSelect->onlyApplyOnEnter = onlyApplyOnEnter;
option.m_CFOSelect->changeFunc = changeFunc;
}
void FrontendOptionAddDynamic(const char* gxtKey, DrawFunc drawFunc, int8 *var, ButtonPressFunc buttonPressFunc, const char* saveName)
{
int8 screenOptionOrder = RegisterNewOption();
FrontendOption& option = customFrontendOptions[numCustomFrontendOptions - 1];
option.type = FEOPTION_SELECT;
TextCopy(option.leftText, leftText);
option.rightTexts = (wchar**)malloc(numRightTexts * sizeof(wchar*));
memcpy(option.rightTexts, rightTexts, numRightTexts * sizeof(wchar*));
option.numRightTexts = numRightTexts;
option.value = var;
option.displayedValue = *var;
option.lastSavedValue = *var;
option.save = saveName;
option.onlyApplyOnEnter = onlyApplyOnEnter;
option.changeFunc = changeFunc;
option.screenOptionOrder = screenOptionOrder;
option.returnPrevPageFunc = returnPrevPageFunc;
}
void FrontendOptionAddDynamic(const wchar* leftText, DrawFunc drawFunc, int8 *var, ButtonPressFunc buttonPressFunc, ReturnPrevPageFunc returnPrevPageFunc, const char* saveName)
{
int8 screenOptionOrder = RegisterNewOption();
FrontendOption& option = customFrontendOptions[numCustomFrontendOptions - 1];
option.type = FEOPTION_DYNAMIC;
option.drawFunc = drawFunc;
option.buttonPressFunc = buttonPressFunc;
TextCopy(option.leftText, leftText);
option.value = var;
option.save = saveName;
option.screenOptionOrder = screenOptionOrder;
option.returnPrevPageFunc = returnPrevPageFunc;
}
void FrontendOptionAddRedirect(const wchar* text, int to, int8 selectedOption, bool fadeIn)
{
int8 screenOptionOrder = RegisterNewOption();
FrontendOption &option = customFrontendOptions[numCustomFrontendOptions - 1];
option.type = FEOPTION_REDIRECT;
option.to = to;
option.option = selectedOption;
option.fadeIn = fadeIn;
TextCopy(option.leftText, text);
option.screenOptionOrder = screenOptionOrder;
option.returnPrevPageFunc = nil;
option.save = nil;
}
void FrontendOptionAddBackButton(const wchar* text, bool fadeIn)
{
int8 screenOptionOrder = RegisterNewOption();
FrontendOption& option = customFrontendOptions[numCustomFrontendOptions - 1];
option.type = FEOPTION_GOBACK;
option.fadeIn = fadeIn;
TextCopy(option.leftText, text);
option.screenOptionOrder = screenOptionOrder;
option.returnPrevPageFunc = nil;
option.save = nil;
CMenuScreenCustom::CMenuEntry &option = aScreens[currentMenu].m_aEntries[screenOptionOrder];
option.m_Action = MENUACTION_CFO_DYNAMIC;
strncpy(option.m_EntryName, gxtKey, 8);
option.m_CFODynamic = new CCFODynamic();
option.m_CFODynamic->drawFunc = drawFunc;
option.m_CFODynamic->buttonPressFunc = buttonPressFunc;
option.m_CFODynamic->value = var;
option.m_CFODynamic->save = saveName;
}
uint8 FrontendScreenAdd(const char* gxtKey, eMenuSprites sprite, int prevPage, int columnWidth, int headerHeight, int lineHeight,
int8 font, float fontScaleX, float fontScaleY, int8 alignment, bool showLeftRightHelper, ReturnPrevPageFunc returnPrevPageFunc) {
uint8 screenOrder = RegisterNewScreen(gxtKey, prevPage);
uint8 screenOrder = RegisterNewScreen(gxtKey, prevPage, returnPrevPageFunc);
FrontendScreen &screen = customFrontendScreens[numCustomFrontendScreens - 1];
screen.id = screenOrder;
screen.sprite = sprite;
screen.prevPage = prevPage;
strncpy(screen.name, gxtKey, 8);
screen.columnWidth = columnWidth;
screen.headerHeight = headerHeight;
screen.lineHeight = lineHeight;
screen.font = font;
screen.fontScaleX = fontScaleX;
screen.fontScaleY = fontScaleY;
screen.alignment = alignment;
screen.returnPrevPageFunc = returnPrevPageFunc;
CCustomScreenLayout *screen = new CCustomScreenLayout();
aScreens[screenOrder].layout = screen;
screen->sprite = sprite;
screen->columnWidth = columnWidth;
screen->headerHeight = headerHeight;
screen->lineHeight = lineHeight;
screen->font = font;
screen->fontScaleX = fontScaleX;
screen->fontScaleY = fontScaleY;
screen->alignment = alignment;
return screenOrder;
}

View File

@ -2,47 +2,23 @@
#include "common.h"
#ifdef CUSTOM_FRONTEND_OPTIONS
#include "Frontend.h"
// Warning:
// All of the code relies on that you won't use more then NUM_MENUROWS(18) options on one page.
// Also congrats if you can make 18 options visible at once.
// About texts:
// All text parameters accept wchar(including hardcoded wchar* and TheText.Get)
// except FrontendScreenAdd(it's char[8] GXT key by the design of Frontend).
// All texts reload if custom options reloaded too, which includes language changes and via live reload feature in debug menu!
// Execute direction:
// All of the calls below eventually manipulate the aScreens array, so keep in mind to add/replace options in order,
// i.e. don't set cursor to 8 first and then 3.
// Live reload:
// You can add/change/undo the new options in-game if you use VS. Change what you want, build the changed bits via "Edit and Continue",
// and hit the "Reload custom frontend options" from debug menu. Or call CustomFrontendOptionsPopulate() from somewhere else.
// ! There are 2 ways to use CFO,
// 1st; by adding a new option to the array in MenuScreensCustom.cpp and passing attributes/CBs to it
// 2nd; by calling the functions listed at the bottom of this file.
// -- Option types
//
// Static/select: You allocate the variable, pass it to function and game sets it from user input among the strings given to function,
// then you can handle ChangeFunc(only called on enter if onlyApplyOnEnter set, or set immediately)
// and ReturnPrevPageFunc optionally. You can store the option in an INI file if you pass the key(as a char array) to corresponding parameter.
// optionally you can add post-change event via ChangeFunc(only called on enter if onlyApplyOnEnter set, or set immediately)
// You can store the option in an INI file if you pass the key(as a char array) to corresponding parameter.
//
// Dynamic: Passing variable to function is only needed if you want to store it, otherwise you should do
// all the operations with ButtonPressFunc, this includes allocating the variable.
// Left-side text is passed while creating and static, but ofc right-side text is dynamic -
// you should return it in DrawFunc, which is called on every draw. ReturnPrevPageFunc is also here if needed.
//
// Redirect: Redirection to another screen. selectedOption parameter is the highlighted option user will see after the redirection.
// you should return it in DrawFunc, which is called on every draw.
//
// Built-in action: As the name suggests, any action that game has built-in. But as an extra you can set the option text,
// and can be informed on button press/focus loss via buttonPressFunc. ReturnPrevPageFunc is also here.
#define FEOPTION_SELECT 0
#define FEOPTION_DYNAMIC 1
#define FEOPTION_REDIRECT 2
#define FEOPTION_GOBACK 3
#define FEOPTION_BUILTIN_ACTION 4
// -- Returned via ButtonPressFunc() action param.
#define FEOPTION_ACTION_LEFT 0
@ -61,7 +37,7 @@
typedef void (*ReturnPrevPageFunc)();
// for static options
typedef void (*ChangeFunc)(int8 displayedValue); // called before updating the value.
typedef void (*ChangeFunc)(int8 before, int8 after); // called after updating the value.
// only called on enter if onlyApplyOnEnter set, otherwise called on every value change
// for dynamic options
@ -69,71 +45,11 @@ typedef wchar* (*DrawFunc)(bool* disabled, bool userHovering); // you must retur
// you can also set *disabled if you want to gray it out.
typedef void (*ButtonPressFunc)(int8 action); // see FEOPTION_ACTIONs above
struct FrontendScreen
{
int id;
char name[8];
eMenuSprites sprite;
int prevPage;
int columnWidth;
int headerHeight;
int lineHeight;
int8 font;
float fontScaleX;
float fontScaleY;
int8 alignment;
bool showLeftRightHelper;
ReturnPrevPageFunc returnPrevPageFunc;
};
struct FrontendOption
{
int8 type;
int8 screenOptionOrder;
int32 screen;
wchar leftText[128];
ReturnPrevPageFunc returnPrevPageFunc;
int8* value;
int8 displayedValue; // only if onlyApplyOnEnter enabled for now
const char* save;
int32 ogOptionId; // for replacements, see overwrite parameter of SetCursor
union {
// Only for dynamic / built-in action
struct {
DrawFunc drawFunc;
ButtonPressFunc buttonPressFunc;
};
// Only for static/select
struct {
wchar** rightTexts;
int8 numRightTexts;
bool onlyApplyOnEnter;
ChangeFunc changeFunc;
int8 lastSavedValue; // only if onlyApplyOnEnter enabled
};
// Only for redirect
struct {
int to;
int8 option;
bool fadeIn;
};
};
};
// -- Internal things
void RemoveCustomFrontendOptions();
void CustomFrontendOptionsPopulate();
extern int lastOgScreen; // for reloading
extern int numCustomFrontendOptions;
extern FrontendOption* customFrontendOptions;
extern int numCustomFrontendScreens;
extern FrontendScreen* customFrontendScreens;
// -- To be used in ButtonPressFunc / ChangeFunc(this one would be weird):
void ChangeScreen(int screen, int option = 0, bool fadeIn = true);
@ -141,6 +57,21 @@ void GoBack(bool fadeIn = true);
uint8 GetNumberOfMenuOptions(int screen);
// !!! We're now moved to MenuScreensCustom.cpp, which houses an array that keeps all original+custom options.
// But you can still use the APIs below, and manipulate aScreens while in game.
// Limits:
// The code relies on that you won't use more then NUM_MENUROWS(18) options on one page, and won't exceed the MENUPAGES of pages.
// Also congrats if you can make 18 options visible at once.
// Texts:
// All text parameters accept char[8] GXT key.
// Execute direction:
// All of the calls below eventually manipulate the aScreens array, so keep in mind to add/replace options in order,
// i.e. don't set cursor to 8 first and then 3.
// -- Placing the cursor to append/overwrite option
//
// Done via FrontendOptionSetCursor(screen, position, overwrite = false), parameters explained below:
@ -152,11 +83,9 @@ uint8 GetNumberOfMenuOptions(int screen);
void FrontendOptionSetCursor(int screen, int8 option, bool overwrite = false);
// var is optional in AddDynamic, enables you to save them in an INI file(also needs passing char array to saveName param. obv), otherwise pass nil/0
void FrontendOptionAddBuiltinAction(const wchar* leftText, int action, ButtonPressFunc buttonPressFunc, ReturnPrevPageFunc returnPrevPageFunc);
void FrontendOptionAddSelect(const wchar* leftText, const wchar** rightTexts, int8 numRightTexts, int8 *var, bool onlyApplyOnEnter, ChangeFunc changeFunc, ReturnPrevPageFunc returnPrevPageFunc, const char* saveName = nil);
void FrontendOptionAddDynamic(const wchar* leftText, DrawFunc rightTextDrawFunc, int8 *var, ButtonPressFunc buttonPressFunc, ReturnPrevPageFunc returnPrevPageFunc, const char* saveName = nil);
void FrontendOptionAddRedirect(const wchar* text, int to, int8 selectedOption = 0, bool fadeIn = true);
void FrontendOptionAddBackButton(const wchar* text, bool fadeIn = true);
void FrontendOptionAddBuiltinAction(const char* gxtKey, int action, int targetMenu = MENUPAGE_NONE, int saveSlot = SAVESLOT_NONE);
void FrontendOptionAddSelect(const char* gxtKey, const char** rightTexts, int8 numRightTexts, int8 *var, bool onlyApplyOnEnter, ChangeFunc changeFunc, const char* saveName = nil);
void FrontendOptionAddDynamic(const char* gxtKey, DrawFunc rightTextDrawFunc, int8 *var, ButtonPressFunc buttonPressFunc, const char* saveName = nil);
uint8 FrontendScreenAdd(const char* gxtKey, eMenuSprites sprite, int prevPage, int columnWidth, int headerHeight, int lineHeight, int8 font, float fontScaleX, float fontScaleY, int8 alignment, bool showLeftRightHelper, ReturnPrevPageFunc returnPrevPageFunc = nil);
#endif