JKSV/source/JKSV.cpp

235 lines
6.9 KiB
C++

#include "JKSV.hpp"
#include "appstates/MainMenuState.hpp"
#include "colors.hpp"
#include "config.hpp"
#include "data/data.hpp"
#include "fslib.hpp"
#include "input.hpp"
#include "logger.hpp"
#include "sdl.hpp"
#include "strings.hpp"
#include "ui/PopMessageManager.hpp"
#include <switch.h>
#define ABORT_ON_FAILURE(x) \
if (!x) \
{ \
return; \
}
namespace
{
constexpr uint8_t BUILD_MON = 5;
constexpr uint8_t BUILD_DAY = 31;
constexpr uint16_t BUILD_YEAR = 2025;
} // namespace
template <typename... Args>
static bool initialize_service(Result (*function)(Args...), const char *serviceName, Args... args)
{
Result error = (*function)(args...);
if (R_FAILED(error))
{
logger::log("Error initializing %s: 0x%X.", error);
return false;
}
return true;
}
JKSV::JKSV(void)
{
// FsLib
ABORT_ON_FAILURE(fslib::initialize());
// This doesn't rely on stdio or anything.
logger::initialize();
// Need to init RomFS here for now until I update FsLib to take care of this.
ABORT_ON_FAILURE(initialize_service(romfsInit, "RomFS"));
// Let FsLib take care of calls to SDMC instead of fs_dev
ABORT_ON_FAILURE(fslib::dev::initialize_sdmc());
// SDL
ABORT_ON_FAILURE(sdl::initialize("JKSV", 1280, 720));
ABORT_ON_FAILURE(sdl::text::initialize());
// Services.
// Using administrator so JKSV can still run in Applet mode.
ABORT_ON_FAILURE(initialize_service(accountInitialize, "Account", AccountServiceType_Administrator));
ABORT_ON_FAILURE(initialize_service(nsInitialize, "NS"));
ABORT_ON_FAILURE(initialize_service(pdmqryInitialize, "PDMQry"));
ABORT_ON_FAILURE(initialize_service(plInitialize, "PL", PlServiceType_User));
ABORT_ON_FAILURE(initialize_service(pmshellInitialize, "PMShell"));
ABORT_ON_FAILURE(initialize_service(setInitialize, "Set"));
ABORT_ON_FAILURE(initialize_service(setsysInitialize, "SetSys"));
ABORT_ON_FAILURE(initialize_service(socketInitializeDefault, "Socket"));
// Input doesn't have anything to return.
input::initialize();
// Neither does config.
config::initialize();
// Get and create working directory. There isn't much of an FS anymore.
fslib::Path workingDirectory = config::get_working_directory();
if (!fslib::directory_exists(workingDirectory) && !fslib::create_directories_recursively(workingDirectory))
{
logger::log("Error creating working directory: %s", fslib::get_error_string());
return;
}
// JKSV also has no internal strings anymore. This is FATAL now.
ABORT_ON_FAILURE(strings::initialize());
if (!data::initialize(false))
{
return;
}
// Install/setup our color changing characters.
sdl::text::add_color_character(L'#', colors::BLUE);
sdl::text::add_color_character(L'*', colors::RED);
sdl::text::add_color_character(L'<', colors::YELLOW);
sdl::text::add_color_character(L'>', colors::GREEN);
sdl::text::add_color_character(L'`', colors::BLUE_GREEN);
sdl::text::add_color_character(L'^', colors::PINK);
// This is to check whether the author wanted credit for their work.
m_showTranslationInfo =
std::char_traits<char>::compare(strings::get_by_name(strings::names::TRANSLATION_INFO, 1), "NULL", 4) != 0;
// This can't be in an initializer list because it needs SDL initialized.
m_headerIcon = sdl::TextureManager::create_load_texture("HeaderIcon", "romfs:/Textures/HeaderIcon.png");
// Push initial main menu state.
JKSV::push_state(std::make_shared<MainMenuState>());
m_isRunning = true;
}
JKSV::~JKSV()
{
// Try to save config first.
config::save();
socketExit();
setsysExit();
setExit();
pmshellExit();
plExit();
pdmqryExit();
nsExit();
accountExit();
sdl::text::exit();
sdl::exit();
fslib::exit();
}
bool JKSV::is_running(void) const
{
return m_isRunning;
}
void JKSV::update(void)
{
input::update();
if (input::button_pressed(HidNpadButton_Plus) && !sm_stateVector.empty() && sm_stateVector.back()->is_closable())
{
m_isRunning = false;
}
JKSV::update_state_vector();
// Update pop messages.
ui::PopMessageManager::update();
}
void JKSV::render(void)
{
sdl::frame_begin(colors::CLEAR_COLOR);
// Top and bottom divider lines.
sdl::render_line(NULL, 30, 88, 1250, 88, colors::WHITE);
sdl::render_line(NULL, 30, 648, 1250, 648, colors::WHITE);
// Icon
m_headerIcon->render(NULL, 66, 27);
// "JKSV"
sdl::text::render(NULL, 130, 32, 34, sdl::text::NO_TEXT_WRAP, colors::WHITE, "JKSV");
// Translation info in bottom left.
if (m_showTranslationInfo)
{
sdl::text::render(NULL,
8,
680,
14,
sdl::text::NO_TEXT_WRAP,
colors::WHITE,
strings::get_by_name(strings::names::TRANSLATION_INFO, 0),
strings::get_by_name(strings::names::TRANSLATION_INFO, 1));
}
// Build date
sdl::text::render(NULL,
8,
700,
14,
sdl::text::NO_TEXT_WRAP,
colors::WHITE,
"v. %02d.%02d.%04d",
BUILD_MON,
BUILD_DAY,
BUILD_YEAR);
// State render loop.
if (!sm_stateVector.empty())
{
for (auto &CurrentState : sm_stateVector)
{
CurrentState->render();
}
}
// Render messages.
ui::PopMessageManager::render();
sdl::frame_end();
}
void JKSV::push_state(std::shared_ptr<AppState> newState)
{
if (!sm_stateVector.empty())
{
sm_stateVector.back()->take_focus();
}
newState->give_focus();
sm_stateVector.push_back(newState);
}
void JKSV::update_state_vector(void)
{
if (sm_stateVector.empty())
{
return;
}
// Check for and purge deactivated states.
for (size_t i = 0; i < sm_stateVector.size(); i++)
{
if (!sm_stateVector.at(i)->is_active())
{
// This is a just in case thing. Some states are never actually purged.
sm_stateVector.at(i)->take_focus();
sm_stateVector.erase(sm_stateVector.begin() + i);
}
}
// Make sure the back has focus.
if (!sm_stateVector.back()->has_focus())
{
sm_stateVector.back()->give_focus();
}
// Only update the back most state.
sm_stateVector.back()->update();
}