isle-portable/CONFIG/vita/src/app.cpp
2025-09-26 19:19:49 +02:00

431 lines
14 KiB
C++

#include <app_settings.h>
#include <iniparser.h>
#include <paf.h>
#include <psp2/appmgr.h>
#include <psp2/io/fcntl.h>
#include <psp2/kernel/clib.h>
#include <psp2/kernel/modulemgr.h>
#include <psp2/sysmodule.h>
int sceLibcHeapSize = 10 * 1024 * 1024;
const char* g_iniPath = "ux0:data/isledecomp/isle/isle.ini";
paf::Framework* g_fw;
paf::Plugin* g_configPlugin;
sce::AppSettings* g_appSettings;
sce::AppSettings::Interface* g_appSetIf;
void merge_dicts(dictionary* dst, dictionary* src)
{
for (int i = 0; i < src->n; i++) {
dictionary_set(dst, src->key[i], src->val[i]);
}
}
struct setting_map {
const char* key_ini;
const char* key_app;
const char type;
};
// mapping from ini key to settings.xml key
const setting_map key_map[] = {
// Game
{"isle:diskpath", "disk_path", 's'},
{"isle:cdpath", "cd_path", 's'},
{"isle:savepath", "save_path", 's'},
{"isle:Transition Type", "transition_type", 'i'},
{"isle:Music", "music", 'b'},
{"isle:3DSound", "3d_sound", 'b'},
// Graphics
{"isle:Island Texture", "island_texture_quality", 'i'},
{"isle:Island Quality", "island_model_quality", 'i'},
//{"isle:Max LOD", "max_lod", 'f'},
//{"isle:Max Allowed Extras", "max_extras", 'i' },
{"isle:MSAA", "msaa", 'i'},
// Controls
{"isle:Touch Scheme", "touch_control_scheme", 'i'},
{"isle:Haptic", "rumble", 'b'},
// Extensions
{"extensions:texture loader", "texture_loader_extension", 'b'},
{"texture loader:texture path", "texture_loader_path", 's'}
};
struct Config {
sce::AppSettings* settings;
dictionary* dict;
char buffer[128];
#define GetDictInt(x, name) x = iniparser_getint(this->dict, name, x)
#define GetDictFloat(x, name) x = iniparser_getdouble(this->dict, name, x)
#define GetDictString(x, name) \
{ \
const char* val = iniparser_getstring(this->dict, name, nullptr); \
if (val != nullptr) { \
x = val; \
} \
}
#define GetDictBool(x, name) x = iniparser_getboolean(this->dict, name, x)
#define SetDictBool(NAME, VALUE) \
{ \
const char* v = VALUE ? "true" : "false"; \
sceClibPrintf("SetIniBool(%s, %s)\n", NAME, v); \
iniparser_set(this->dict, NAME, v); \
}
#define SetDictInt(NAME, VALUE) \
{ \
sceClibSnprintf(buffer, sizeof(buffer), "%d", VALUE); \
sceClibPrintf("SetIniInt(%s, %d)\n", NAME, VALUE); \
iniparser_set(this->dict, NAME, buffer); \
}
#define SetDictFloat(NAME, VALUE) \
{ \
sceClibSnprintf(buffer, sizeof(buffer), "%f", VALUE); \
sceClibPrintf("SetIniFloat(%s, %f)\n", NAME, VALUE); \
iniparser_set(this->dict, NAME, buffer); \
}
#define SetDictString(NAME, VALUE) \
{ \
sceClibPrintf("SetString(%s, %s)\n", NAME, VALUE); \
iniparser_set(this->dict, NAME, VALUE); \
}
void Init(sce::AppSettings* settings)
{
this->settings = settings;
dict = dictionary_new(0);
// set defaults
iniparser_set(this->dict, "isle", nullptr);
iniparser_set(dict, "extensions", NULL);
iniparser_set(this->dict, "texture loader", nullptr);
SetDictString("isle:diskpath", "ux0:data/isledecomp/isle/disk");
SetDictString("isle:cdpath", "ux0:data/isledecomp/isle/cd");
SetDictString("isle:savepath", "ux0:data/isledecomp/isle");
SetDictInt("isle:MSAA", 4);
SetDictInt("isle:Display Bit Depth", 32);
SetDictBool("isle:Flip Surfaces", false);
SetDictBool("isle:Full Screen", true);
SetDictBool("isle:Exclusive Full Screen", true);
SetDictBool("isle:Wide View Angle", true);
SetDictInt("isle:Transition Type", 3); // 3: Mosaic
SetDictInt("isle:Touch Scheme", 2);
SetDictBool("isle:3DSound", true);
SetDictBool("isle:Music", true);
SetDictBool("isle:Haptic", true);
SetDictBool("isle:UseJoystick", true);
SetDictInt("isle:JoystickIndex", 0);
SetDictBool("isle:Draw Cursor", true);
SetDictBool("extensions:texture loader", false);
SetDictString("texture loader:texture path", "textures/");
SetDictBool("isle:Back Buffers in Video RAM", true);
SetDictInt("isle:Island Quality", 2);
SetDictInt("isle:Island Texture", 1);
SetDictInt("isle:MSAA", 4);
SetDictFloat("isle:Max LOD", 3.5);
SetDictInt("isle:Max Allowed Extras", 20);
SetDictInt("isle:Aspect Ratio", 0);
SetDictInt("isle:Horizontal Resolution", 640);
SetDictInt("isle:Vertical Resolution", 480);
SetDictFloat("isle:Frame Delta", 10.0f);
}
void LoadIni()
{
dictionary* ini = iniparser_load(g_iniPath);
if (ini) {
merge_dicts(this->dict, ini);
iniparser_freedict(ini);
}
}
bool SaveIni()
{
FILE* fd = fopen(g_iniPath, "w");
if (fd) {
iniparser_dump_ini(this->dict, fd);
}
else {
sceClibPrintf("failed to write isle.ini\n");
}
return true;
}
void ToSettings()
{
const int len = sizeof(key_map) / sizeof(key_map[0]);
for (int i = 0; i < len; i++) {
const setting_map m = key_map[i];
switch (m.type) {
case 'f': // float, AppSettings doesnt have float so just use string
case 's': {
const char* value = iniparser_getstring(this->dict, m.key_ini, "");
this->settings->SetString(m.key_app, value);
sceClibPrintf("ini->settings %s = %s\n", m.key_app, value);
break;
}
case 'i': {
int32_t value = iniparser_getint(this->dict, m.key_ini, 0);
this->settings->SetInt(m.key_app, value);
sceClibPrintf("ini->settings %s = %d\n", m.key_app, value);
break;
}
case 'b': {
bool value = iniparser_getboolean(this->dict, m.key_ini, 0) == 1;
this->settings->SetBool(m.key_app, value);
sceClibPrintf("ini->settings %s = %s\n", m.key_app, value ? "true" : "false");
break;
}
default: {
sceClibPrintf("invalid setting map entry %s %s %c\n", m.key_app, m.key_ini, m.type);
}
}
}
}
void FromSettings()
{
const int len = sizeof(key_map) / sizeof(key_map[0]);
for (int i = 0; i < len; i++) {
const setting_map m = key_map[i];
switch (m.type) {
case 'f': // float, AppSettings doesnt have float so just use string
case 's': {
const char* def = iniparser_getstring(this->dict, m.key_ini, "");
this->settings->GetString(m.key_app, this->buffer, sizeof(this->buffer), def);
iniparser_set(this->dict, m.key_ini, buffer);
sceClibPrintf("settings->ini %s = %s\n", m.key_ini, buffer);
break;
}
case 'i': {
int32_t value = iniparser_getint(this->dict, m.key_ini, 0);
this->settings->GetInt(m.key_app, &value, value);
SetDictInt(m.key_ini, value);
sceClibPrintf("settings->ini %s = %d\n", m.key_ini, value);
break;
}
case 'b': {
bool value = iniparser_getboolean(this->dict, m.key_ini, 0) == 1;
this->settings->GetBool(m.key_app, &value, value);
SetDictBool(m.key_ini, value);
sceClibPrintf("settings->ini %s = %s\n", m.key_ini, value ? "true" : "false");
break;
}
default: {
sceClibPrintf("invalid setting map entry %s %s %c\n", m.key_app, m.key_ini, m.type);
}
}
}
}
};
Config g_config;
paf::Plugin* load_config_plugin(paf::Framework* paf_fw)
{
paf::Plugin::InitParam pluginParam;
pluginParam.name = "config_plugin";
pluginParam.caller_name = "__main__";
pluginParam.resource_file = "app0:/config_plugin.rco";
pluginParam.init_func = NULL;
pluginParam.start_func = NULL;
pluginParam.stop_func = NULL;
pluginParam.exit_func = NULL;
paf::Plugin::LoadSync(pluginParam);
return paf_fw->FindPlugin("config_plugin");
}
int load_app_settings_plugin()
{
paf::Plugin::InitParam pluginParam;
sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_INTERNAL_BXCE);
sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_INTERNAL_INI_FILE_PROCESSOR);
sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_INTERNAL_COMMON_GUI_DIALOG);
pluginParam.name = "app_settings_plugin";
pluginParam.resource_file = "vs0:vsh/common/app_settings_plugin.rco";
pluginParam.caller_name = "__main__";
pluginParam.set_param_func = sce::AppSettings::PluginSetParamCB;
pluginParam.init_func = sce::AppSettings::PluginInitCB;
pluginParam.start_func = sce::AppSettings::PluginStartCB;
pluginParam.stop_func = sce::AppSettings::PluginStopCB;
pluginParam.exit_func = sce::AppSettings::PluginExitCB;
pluginParam.module_file = "vs0:vsh/common/app_settings.suprx";
pluginParam.draw_priority = 0x96;
paf::Plugin::LoadSync(pluginParam);
return 0;
}
int exit_type = 0;
void save_and_exit()
{
g_config.FromSettings();
g_config.SaveIni();
g_fw->RequestShutdown();
exit_type = 1;
}
void save_and_launch()
{
g_config.FromSettings();
g_config.SaveIni();
g_fw->RequestShutdown();
exit_type = 2;
}
void CBOnStartPageTransition(const char* elementId, int32_t type)
{
}
void CBOnPageActivate(const char* elementId, int32_t type)
{
}
void CBOnPageDeactivate(const char* elementId, int32_t type)
{
}
int32_t CBOnCheckVisible(const char* elementId, bool* pIsVisible)
{
*pIsVisible = true;
return SCE_OK;
}
int32_t CBOnPreCreate(const char* elementId, sce::AppSettings::Element* element)
{
return SCE_OK;
}
int32_t CBOnPostCreate(const char* elementId, paf::ui::Widget* widget)
{
return SCE_OK;
}
int32_t CBOnPress(const char* elementId, const char* newValue)
{
if (sce_paf_strcmp(elementId, "save_exit_button") == 0) {
save_and_exit();
return SCE_OK;
}
if (sce_paf_strcmp(elementId, "save_launch_button") == 0) {
save_and_launch();
return SCE_OK;
}
sceClibPrintf("OnPress %s %s\n", elementId, newValue);
return SCE_OK;
}
int32_t CBOnPress2(const char* elementId, const char* newValue)
{
return SCE_OK;
}
void CBOnTerm(int32_t result)
{
if (exit_type == 0) {
sceKernelExitProcess(0);
}
}
const wchar_t* CBOnGetString(const char* elementId)
{
wchar_t* res = g_configPlugin->GetString(elementId);
if (res[0] != 0) {
return res;
}
return L"unknown string";
}
int32_t CBOnGetSurface(paf::graph::Surface** surf, const char* elementId)
{
return SCE_OK;
}
void open_settings()
{
g_config.Init(g_appSettings);
g_config.LoadIni();
g_config.ToSettings();
sce::AppSettings::InterfaceCallbacks ifCb;
ifCb.onStartPageTransitionCb = CBOnStartPageTransition;
ifCb.onPageActivateCb = CBOnPageActivate;
ifCb.onPageDeactivateCb = CBOnPageDeactivate;
ifCb.onCheckVisible = CBOnCheckVisible;
ifCb.onPreCreateCb = CBOnPreCreate;
ifCb.onPostCreateCb = CBOnPostCreate;
ifCb.onPressCb = CBOnPress;
ifCb.onPressCb2 = CBOnPress2;
ifCb.onTermCb = CBOnTerm;
ifCb.onGetStringCb = (sce::AppSettings::InterfaceCallbacks::GetStringCallback) CBOnGetString;
ifCb.onGetSurfaceCb = CBOnGetSurface;
paf::wstring msg_save_exit(g_configPlugin->GetString("msg_save_exit"));
paf::wstring msg_save_launch(g_configPlugin->GetString("msg_save_launch"));
paf::wstring msg_exit(g_configPlugin->GetString("msg_exit"));
paf::Plugin* appSetPlug = paf::Plugin::Find("app_settings_plugin");
g_appSetIf = (sce::AppSettings::Interface*) appSetPlug->GetInterface(1);
g_appSetIf->Show(&ifCb);
g_appSetIf->AddFooterButton("save_exit_button", &msg_save_exit, 1);
g_appSetIf->AddFooterButton("save_launch_button", &msg_save_launch, 2);
g_appSetIf->ShowFooter();
}
int main()
{
paf::Framework::InitParam fwParam;
fwParam.mode = paf::Framework::Mode_Normal;
paf::Framework* paf_fw = new paf::Framework(fwParam);
g_fw = paf_fw;
paf_fw->LoadCommonResourceSync();
load_app_settings_plugin();
paf::Plugin* configPlugin = load_config_plugin(paf_fw);
g_configPlugin = configPlugin;
configPlugin->SetLocale(Locale_EN);
size_t fileSize = 0;
const char* mimeType = nullptr;
auto settingsXmlFile = configPlugin->GetResource()->GetFile("settings.xml", &fileSize, &mimeType);
sce::AppSettings::InitParam settingsParam;
settingsParam.xml_file = settingsXmlFile;
settingsParam.alloc_cb = sce_paf_malloc;
settingsParam.free_cb = sce_paf_free;
settingsParam.realloc_cb = sce_paf_realloc;
settingsParam.safemem_offset = 0;
settingsParam.safemem_size = 0x400;
sce::AppSettings::GetInstance(settingsParam, &g_appSettings);
g_appSettings->Initialize();
open_settings();
paf_fw->Run();
if (exit_type == 2) {
int ret = sceAppMgrLoadExec("app0:/eboot.bin", NULL, NULL);
printf("sceAppMgrLoadExec: %08x\n", ret);
}
return 0;
}