mirror of
https://github.com/Vita3K/Vita3K-Android.git
synced 2024-11-23 13:39:48 +00:00
host: Move several items from host to emulator:
* init * handle_events * screen_render.cpp and screen_render.h * app.cpp and app.h * config functions * discrete GPU exports * emulator-called SFO functions
This commit is contained in:
parent
a5502792b0
commit
6dcc7633ea
@ -76,14 +76,24 @@ add_subdirectory(gui)
|
||||
add_executable(
|
||||
emulator
|
||||
MACOSX_BUNDLE
|
||||
app.cpp
|
||||
app.h
|
||||
config.cpp
|
||||
config.h
|
||||
init.cpp
|
||||
main.cpp
|
||||
performance.cpp
|
||||
screen_render.cpp
|
||||
screen_render.h
|
||||
sfo.cpp
|
||||
sfo.h
|
||||
)
|
||||
|
||||
target_link_libraries(emulator PRIVATE ${Boost_LIBRARIES} cpu dialog elfio glbinding-aux glutil gui host shader imgui modules nids util vita-toolchain yaml-cpp)
|
||||
if(LINUX)
|
||||
target_link_libraries(emulator PRIVATE ${Boost_LIBRARIES} cpu dialog elfio glutil gui host shader imgui modules nids vita-toolchain util stdc++fs)
|
||||
else()
|
||||
target_link_libraries(emulator PRIVATE ${Boost_LIBRARIES} cpu dialog elfio glutil gui host shader imgui modules nids vita-toolchain util)
|
||||
target_link_libraries(emulator PRIVATE stdc++fs)
|
||||
endif()
|
||||
|
||||
set_target_properties(emulator PROPERTIES OUTPUT_NAME Vita3K
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
|
||||
|
@ -15,16 +15,19 @@
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
#include "app.h"
|
||||
|
||||
#include "sfo.h"
|
||||
|
||||
#include <gui/functions.h>
|
||||
#include <gui/imgui_impl_sdl_gl3.h>
|
||||
#include <host/app.h>
|
||||
#include <host/functions.h>
|
||||
#include <host/sfo.h>
|
||||
#include <host/state.h>
|
||||
#include <host/version.h>
|
||||
#include <io/functions.h>
|
||||
#include <io/io.h>
|
||||
#include <io/state.h>
|
||||
#include <kernel/functions.h>
|
||||
#include <kernel/load_self.h>
|
||||
#include <kernel/state.h>
|
||||
#include <kernel/thread/thread_functions.h>
|
||||
@ -48,6 +51,47 @@ static const char *EBOOT_PATH_ABS = "app0:eboot.bin";
|
||||
|
||||
using namespace gl;
|
||||
|
||||
static void handle_window_event(HostState &state, const SDL_WindowEvent event) {
|
||||
switch (static_cast<SDL_WindowEventID>(event.event)) {
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
update_viewport(state);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool handle_events(HostState &host) {
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
ImGui_ImplSdlGL3_ProcessEvent(&event);
|
||||
switch (event.type) {
|
||||
case SDL_QUIT:
|
||||
stop_all_threads(host.kernel);
|
||||
host.gxm.display_queue.abort();
|
||||
host.display.abort.exchange(true);
|
||||
host.display.condvar.notify_all();
|
||||
return false;
|
||||
|
||||
case SDL_KEYDOWN:
|
||||
if (event.key.keysym.sym == SDLK_g) {
|
||||
auto &display = host.display;
|
||||
|
||||
// toggle gui state
|
||||
bool old_imgui_render = display.imgui_render.load();
|
||||
while (!display.imgui_render.compare_exchange_weak(old_imgui_render, !old_imgui_render)) {
|
||||
}
|
||||
}
|
||||
|
||||
case SDL_WINDOWEVENT:
|
||||
handle_window_event(host, event.window);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void error_dialog(const std::string &message, SDL_Window *window) {
|
||||
if (SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", message.c_str(), window) < 0) {
|
||||
LOG_ERROR("SDL Error: {}", message);
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
struct Config;
|
||||
struct HostState;
|
||||
struct SDL_Window;
|
||||
template <class T>
|
||||
@ -47,6 +48,9 @@ enum class AppRunType {
|
||||
Vpk,
|
||||
};
|
||||
|
||||
bool init(HostState &state, Config cfg);
|
||||
void update_viewport(HostState &state);
|
||||
bool handle_events(HostState &host);
|
||||
void error_dialog(const std::string &message, SDL_Window *window = nullptr);
|
||||
ExitCode load_app(Ptr<const void> &entry_point, HostState &host, const std::wstring &path, AppRunType run_type);
|
||||
ExitCode run_app(HostState &host, Ptr<const void> &entry_point);
|
@ -15,7 +15,7 @@
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
#include <host/config.h>
|
||||
#include "config.h"
|
||||
|
||||
#include <host/version.h>
|
||||
#include <util/log.h>
|
||||
@ -111,7 +111,7 @@ bool serialize(Config &cfg) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool deserialize(Config &cfg) {
|
||||
static bool deserialize(Config &cfg) {
|
||||
YAML::Node config_node{};
|
||||
|
||||
try {
|
45
src/emulator/config.h
Normal file
45
src/emulator/config.h
Normal file
@ -0,0 +1,45 @@
|
||||
// Vita3K emulator project
|
||||
// Copyright (C) 2018 Vita3K team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <host/config.h>
|
||||
|
||||
namespace config {
|
||||
|
||||
enum class InitResult {
|
||||
OK,
|
||||
QUIT,
|
||||
INCORRECT_ARGS,
|
||||
};
|
||||
|
||||
/*
|
||||
* \brief Save emulator config to a YML file.
|
||||
*
|
||||
* \return True on saving success.
|
||||
*/
|
||||
bool serialize(Config &cfg);
|
||||
|
||||
/**
|
||||
* \brief Initializes config system, parsing command-line args and handling some basic ones:
|
||||
* --help, --version, --log-level
|
||||
* \param cfg Config options are returned via this parameter.
|
||||
* \return True on success, false on error.
|
||||
*/
|
||||
InitResult init(Config &cfg, int argc, char **argv);
|
||||
|
||||
} // namespace config
|
@ -1,18 +1,13 @@
|
||||
add_library(
|
||||
host
|
||||
STATIC
|
||||
include/host/app.h
|
||||
include/host/config.h
|
||||
include/host/screen_render.h
|
||||
include/host/functions.h
|
||||
include/host/imports.h
|
||||
include/host/sfo.h
|
||||
include/host/state.h
|
||||
include/host/version.h
|
||||
include/host/app_util.h
|
||||
src/app.cpp
|
||||
src/screen_render.cpp
|
||||
src/config.cpp
|
||||
src/host.cpp
|
||||
src/sfo.cpp
|
||||
version.cpp
|
||||
@ -22,4 +17,4 @@ configure_file(src/version.cpp.in version.cpp)
|
||||
|
||||
target_include_directories(host PUBLIC include)
|
||||
target_link_libraries(host PUBLIC audio cpu ctrl gxm io kernel mem net nids np renderer rtc util gui)
|
||||
target_link_libraries(host PRIVATE ${Boost_LIBRARIES} glbinding-aux glutil microprofile sdl2 yaml-cpp)
|
||||
target_link_libraries(host PRIVATE ${Boost_LIBRARIES} glutil microprofile sdl2)
|
||||
|
@ -40,36 +40,3 @@ struct Config {
|
||||
optional<std::string> pref_path;
|
||||
bool archive_log = false;
|
||||
};
|
||||
|
||||
namespace config {
|
||||
|
||||
enum class InitResult {
|
||||
OK,
|
||||
QUIT,
|
||||
INCORRECT_ARGS,
|
||||
};
|
||||
|
||||
/*
|
||||
* \brief Initializes config system from a YML file.
|
||||
*
|
||||
* \param cfg Config options are returned via this parameter.
|
||||
* \return True on loading success.
|
||||
*/
|
||||
bool deserialize(Config &cfg);
|
||||
|
||||
/*
|
||||
* \brief Save emulator config to a YML file.
|
||||
*
|
||||
* \return True on saving success.
|
||||
*/
|
||||
bool serialize(Config &cfg);
|
||||
|
||||
/**
|
||||
* \brief Initializes config system, parsing command-line args and handling some basic ones:
|
||||
* --help, --version, --log-level
|
||||
* \param cfg Config options are returned via this parameter.
|
||||
* \return True on success, false on error.
|
||||
*/
|
||||
InitResult init(Config &cfg, int argc, char **argv);
|
||||
|
||||
} // namespace config
|
||||
|
@ -19,10 +19,11 @@
|
||||
|
||||
#include <psp2/types.h>
|
||||
|
||||
struct Config;
|
||||
#include <string>
|
||||
|
||||
struct CPUState;
|
||||
struct HostState;
|
||||
struct SfoFile;
|
||||
|
||||
bool init(HostState &state, Config cfg);
|
||||
bool handle_events(HostState &host);
|
||||
void call_import(HostState &host, CPUState &cpu, uint32_t nid, SceUID thread_id);
|
||||
bool get_data(std::string &out_data, SfoFile &file, int id);
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include <utility> // pair
|
||||
#include <vector>
|
||||
|
||||
struct SfoHeader {
|
||||
@ -48,7 +48,3 @@ struct SfoFile {
|
||||
|
||||
std::vector<SfoEntry> entries;
|
||||
};
|
||||
|
||||
bool load_sfo(SfoFile &file, const std::vector<uint8_t> &data);
|
||||
bool find_data(std::string &out_data, SfoFile &file, const std::string &key);
|
||||
bool get_data(std::string &out_data, SfoFile &file, int id);
|
||||
|
@ -17,69 +17,16 @@
|
||||
|
||||
#include <host/functions.h>
|
||||
|
||||
#include <cpu/functions.h>
|
||||
#include <host/imports.h>
|
||||
#include <host/state.h>
|
||||
#include <host/version.h>
|
||||
|
||||
#include <audio/functions.h>
|
||||
#include <cpu/functions.h>
|
||||
#include <glutil/gl.h>
|
||||
#include <io/functions.h>
|
||||
#include <io/io.h>
|
||||
#include <kernel/functions.h>
|
||||
#include <kernel/thread/thread_state.h>
|
||||
#include <nids/functions.h>
|
||||
#include <rtc/rtc.h>
|
||||
#include <util/lock_and_find.h>
|
||||
#include <util/log.h>
|
||||
|
||||
#include <SDL_events.h>
|
||||
#include <SDL_filesystem.h>
|
||||
#include <SDL_video.h>
|
||||
|
||||
#include <glbinding-aux/types_to_string.h>
|
||||
#include <glbinding/Binding.h>
|
||||
#include <microprofile.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
#include <gui/imgui_impl_sdl_gl3.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#include <dirent.h>
|
||||
#include <util/string_utils.h>
|
||||
#else
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
// Use discrete GPU by default
|
||||
|
||||
extern "C" {
|
||||
// NVIDIA Optimus (Driver: 302+)
|
||||
// See: http://developer.download.nvidia.com/devzone/devcenter/gamegraphics/files/OptimusRenderingPolicies.pdf
|
||||
__declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
|
||||
// AMD (Driver: 13.35+)
|
||||
// See: https://gpuopen.com/amdpowerxpressrequesthighperformance/
|
||||
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
using namespace glbinding;
|
||||
|
||||
static bool LOG_IMPORT_CALLS = false;
|
||||
static constexpr bool LOG_UNK_NIDS_ALWAYS = false;
|
||||
|
||||
static constexpr auto DEFAULT_RES_WIDTH = 960;
|
||||
static constexpr auto DEFAULT_RES_HEIGHT = 544;
|
||||
|
||||
#define NID(name, nid) extern const ImportFn import_##name;
|
||||
#include <nids/nids.h>
|
||||
#undef NID
|
||||
@ -96,196 +43,6 @@ static ImportFn resolve_import(uint32_t nid) {
|
||||
return ImportFn();
|
||||
}
|
||||
|
||||
#if MICROPROFILE_ENABLED
|
||||
static void before_callback(const glbinding::FunctionCall &fn) {
|
||||
const MicroProfileToken token = MicroProfileGetToken("OpenGL", fn.function->name(), MP_CYAN, MicroProfileTokenTypeCpu);
|
||||
MICROPROFILE_ENTER_TOKEN(token);
|
||||
}
|
||||
#endif // MICROPROFILE_ENABLED
|
||||
|
||||
static void after_callback(const glbinding::FunctionCall &fn) {
|
||||
MICROPROFILE_LEAVE();
|
||||
for (GLenum error = glGetError(); error != GL_NO_ERROR; error = glGetError()) {
|
||||
std::stringstream gl_error;
|
||||
gl_error << error;
|
||||
LOG_ERROR("OpenGL: {} set error {}.", fn.function->name(), gl_error.str());
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void update_viewport(HostState &state) {
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
SDL_GL_GetDrawableSize(state.window.get(), &w, &h);
|
||||
|
||||
state.drawable_size.x = w;
|
||||
state.drawable_size.y = h;
|
||||
|
||||
if (h > 0) {
|
||||
const float window_aspect = static_cast<float>(w) / h;
|
||||
const float vita_aspect = static_cast<float>(DEFAULT_RES_WIDTH) / DEFAULT_RES_HEIGHT;
|
||||
if (window_aspect > vita_aspect) {
|
||||
// Window is wide. Pin top and bottom.
|
||||
state.viewport_size.x = h * vita_aspect;
|
||||
state.viewport_size.y = h;
|
||||
state.viewport_pos.x = (w - state.viewport_size.x) / 2;
|
||||
state.viewport_pos.y = 0;
|
||||
} else {
|
||||
// Window is tall. Pin left and right.
|
||||
state.viewport_size.x = w;
|
||||
state.viewport_size.y = w / vita_aspect;
|
||||
state.viewport_pos.x = 0;
|
||||
state.viewport_pos.y = (h - state.viewport_size.y) / 2;
|
||||
}
|
||||
} else {
|
||||
state.viewport_pos.x = 0;
|
||||
state.viewport_pos.y = 0;
|
||||
state.viewport_size.x = 0;
|
||||
state.viewport_size.y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_window_event(HostState &state, const SDL_WindowEvent event) {
|
||||
switch (static_cast<SDL_WindowEventID>(event.event)) {
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
update_viewport(state);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool init(HostState &state, Config cfg) {
|
||||
const std::unique_ptr<char, void (&)(void *)> base_path(SDL_GetBasePath(), SDL_free);
|
||||
const std::unique_ptr<char, void (&)(void *)> pref_path(SDL_GetPrefPath(org_name, app_name), SDL_free);
|
||||
|
||||
const ResumeAudioThread resume_thread = [&state](SceUID thread_id) {
|
||||
const ThreadStatePtr thread = lock_and_find(thread_id, state.kernel.threads, state.kernel.mutex);
|
||||
const std::lock_guard<std::mutex> lock(thread->mutex);
|
||||
if (thread->to_do == ThreadToDo::wait) {
|
||||
thread->to_do = ThreadToDo::run;
|
||||
}
|
||||
thread->something_to_do.notify_all();
|
||||
};
|
||||
|
||||
state.cfg = std::move(cfg);
|
||||
state.base_path = base_path.get();
|
||||
|
||||
// If configuration already provides preference path
|
||||
if (!state.cfg.pref_path) {
|
||||
state.pref_path = pref_path.get();
|
||||
state.cfg.pref_path = state.pref_path;
|
||||
} else {
|
||||
state.pref_path = state.cfg.pref_path.value();
|
||||
}
|
||||
|
||||
state.window = WindowPtr(SDL_CreateWindow(window_title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, DEFAULT_RES_WIDTH, DEFAULT_RES_HEIGHT, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE), SDL_DestroyWindow);
|
||||
if (!state.window || !init(state.mem) || !init(state.audio, resume_thread) || !init(state.io, state.pref_path.c_str(), state.base_path.c_str())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
|
||||
state.glcontext = GLContextPtr(SDL_GL_CreateContext(state.window.get()), SDL_GL_DeleteContext);
|
||||
if (!state.glcontext) {
|
||||
LOG_ERROR("Could not create OpenGL context.");
|
||||
return false;
|
||||
}
|
||||
|
||||
update_viewport(state);
|
||||
|
||||
// Try adaptive vsync first, falling back to regular vsync.
|
||||
if (SDL_GL_SetSwapInterval(-1) < 0) {
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
}
|
||||
LOG_INFO("Swap interval = {}", SDL_GL_GetSwapInterval());
|
||||
|
||||
const glbinding::GetProcAddress get_proc_address = [](const char *name) {
|
||||
return reinterpret_cast<ProcAddress>(SDL_GL_GetProcAddress(name));
|
||||
};
|
||||
Binding::initialize(get_proc_address, false);
|
||||
Binding::setCallbackMaskExcept(CallbackMask::Before | CallbackMask::After, { "glGetError" });
|
||||
#if MICROPROFILE_ENABLED != 0
|
||||
Binding::setBeforeCallback(before_callback);
|
||||
#endif // MICROPROFILE_ENABLED
|
||||
Binding::setAfterCallback(after_callback);
|
||||
|
||||
state.kernel.base_tick = { rtc_base_ticks() };
|
||||
|
||||
std::string dir_path = state.pref_path + "ux0/app";
|
||||
#ifdef WIN32
|
||||
_WDIR *d = _wopendir((string_utils::utf_to_wide(dir_path)).c_str());
|
||||
_wdirent *dp;
|
||||
#else
|
||||
DIR *d = opendir(dir_path.c_str());
|
||||
dirent *dp;
|
||||
#endif
|
||||
do {
|
||||
std::string d_name_utf8;
|
||||
#ifdef WIN32
|
||||
if ((dp = _wreaddir(d)) != NULL) {
|
||||
d_name_utf8 = string_utils::wide_to_utf(dp->d_name);
|
||||
#else
|
||||
if ((dp = readdir(d)) != NULL) {
|
||||
d_name_utf8 = dp->d_name;
|
||||
#endif
|
||||
if ((strcmp(d_name_utf8.c_str(), ".")) && (strcmp(d_name_utf8.c_str(), ".."))) {
|
||||
vfs::FileBuffer params;
|
||||
state.io.title_id = d_name_utf8;
|
||||
if (vfs::read_app_file(params, state.pref_path, state.io.title_id, "sce_sys/param.sfo")) {
|
||||
SfoFile sfo_handle;
|
||||
load_sfo(sfo_handle, params);
|
||||
find_data(state.game_version, sfo_handle, "APP_VER");
|
||||
find_data(state.game_title, sfo_handle, "TITLE");
|
||||
std::replace(state.game_title.begin(), state.game_title.end(), '\n', ' ');
|
||||
state.gui.game_selector.games.push_back({ state.game_version, state.game_title, state.io.title_id });
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (dp);
|
||||
|
||||
#ifdef WIN32
|
||||
_wclosedir(d);
|
||||
#else
|
||||
closedir(d);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool handle_events(HostState &host) {
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
ImGui_ImplSdlGL3_ProcessEvent(&event);
|
||||
switch (event.type) {
|
||||
case SDL_QUIT:
|
||||
stop_all_threads(host.kernel);
|
||||
host.gxm.display_queue.abort();
|
||||
host.display.abort.exchange(true);
|
||||
host.display.condvar.notify_all();
|
||||
return false;
|
||||
|
||||
case SDL_KEYDOWN:
|
||||
if (event.key.keysym.sym == SDLK_g) {
|
||||
auto &display = host.display;
|
||||
|
||||
// toggle gui state
|
||||
bool old_imgui_render = display.imgui_render.load();
|
||||
while (!display.imgui_render.compare_exchange_weak(old_imgui_render, !old_imgui_render)) {
|
||||
}
|
||||
}
|
||||
|
||||
case SDL_WINDOWEVENT:
|
||||
handle_window_event(host, event.window);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Resolves a function imported from a loaded module.
|
||||
* \param kernel Kernel state struct
|
||||
|
@ -15,67 +15,10 @@
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
#include <host/functions.h>
|
||||
|
||||
#include <host/sfo.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
bool load_sfo(SfoFile &sfile, const std::vector<uint8_t> &content) {
|
||||
if (content.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(static_cast<void *>(&sfile.header), content.data(), sizeof(SfoHeader));
|
||||
|
||||
sfile.entries.resize(sfile.header.tables_entries + 1);
|
||||
|
||||
for (uint32_t i = 0; i < sfile.header.tables_entries; i++) {
|
||||
memcpy(static_cast<void *>(&sfile.entries[i].entry), content.data() + sizeof(SfoHeader) + i * sizeof(SfoIndexTableEntry), sizeof(SfoIndexTableEntry));
|
||||
}
|
||||
|
||||
sfile.entries[sfile.header.tables_entries].entry.key_offset = sfile.header.data_table_start - sfile.header.key_table_start;
|
||||
|
||||
for (uint32_t i = 0; i < sfile.header.tables_entries; i++) {
|
||||
uint32_t keySize = sfile.entries[i + 1].entry.key_offset - sfile.entries[i].entry.key_offset;
|
||||
|
||||
sfile.entries[i].data.first.resize(keySize);
|
||||
|
||||
memcpy(&sfile.entries[i].data.first[0], &content[sfile.header.key_table_start + sfile.entries[i].entry.key_offset], keySize);
|
||||
|
||||
//Quick hack to remove garbage null terminator caused by reading directly
|
||||
//to buffer
|
||||
sfile.entries[i].data.first = sfile.entries[i].data.first.c_str();
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < sfile.header.tables_entries; i++) {
|
||||
uint32_t dataSize = sfile.entries[i].entry.data_len;
|
||||
|
||||
sfile.entries[i].data.second.resize(dataSize);
|
||||
|
||||
// The last of data is a terminator
|
||||
memcpy(&sfile.entries[i].data.second[0], &content[sfile.header.data_table_start + sfile.entries[i].entry.data_offset], dataSize - 1);
|
||||
|
||||
//Quick hack to remove garbage null terminator caused by reading directly
|
||||
//to buffer
|
||||
sfile.entries[i].data.second = sfile.entries[i].data.second.c_str();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool find_data(std::string &out_data, SfoFile &file, const std::string &key) {
|
||||
auto res = std::find_if(file.entries.begin(), file.entries.end(),
|
||||
[key](auto et) { return et.data.first == key; });
|
||||
|
||||
if (res == file.entries.end()) {
|
||||
return false;
|
||||
}
|
||||
out_data = res->data.second;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool get_data(std::string &out_data, SfoFile &file, int id) {
|
||||
if (file.entries.size() < id) {
|
||||
return false;
|
||||
|
201
src/emulator/init.cpp
Normal file
201
src/emulator/init.cpp
Normal file
@ -0,0 +1,201 @@
|
||||
// Vita3K emulator project
|
||||
// Copyright (C) 2018 Vita3K team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
#include "app.h"
|
||||
#include "sfo.h"
|
||||
|
||||
#include <audio/functions.h>
|
||||
#include <glutil/gl.h>
|
||||
#include <host/state.h>
|
||||
#include <host/version.h>
|
||||
#include <io/functions.h>
|
||||
#include <io/io.h> // vfs
|
||||
#include <rtc/rtc.h>
|
||||
#include <util/lock_and_find.h>
|
||||
#include <util/log.h>
|
||||
|
||||
#include <SDL_filesystem.h>
|
||||
#include <SDL_video.h>
|
||||
#include <glbinding-aux/types_to_string.h>
|
||||
#include <glbinding/Binding.h>
|
||||
#include <microprofile.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#ifdef WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#include <dirent.h>
|
||||
#include <util/string_utils.h>
|
||||
#else
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
using namespace glbinding;
|
||||
|
||||
static constexpr auto DEFAULT_RES_WIDTH = 960;
|
||||
static constexpr auto DEFAULT_RES_HEIGHT = 544;
|
||||
|
||||
#if MICROPROFILE_ENABLED
|
||||
static void before_callback(const glbinding::FunctionCall &fn) {
|
||||
const MicroProfileToken token = MicroProfileGetToken("OpenGL", fn.function->name(), MP_CYAN, MicroProfileTokenTypeCpu);
|
||||
MICROPROFILE_ENTER_TOKEN(token);
|
||||
}
|
||||
#endif // MICROPROFILE_ENABLED
|
||||
|
||||
static void after_callback(const glbinding::FunctionCall &fn) {
|
||||
MICROPROFILE_LEAVE();
|
||||
for (GLenum error = glGetError(); error != GL_NO_ERROR; error = glGetError()) {
|
||||
std::stringstream gl_error;
|
||||
gl_error << error;
|
||||
LOG_ERROR("OpenGL: {} set error {}.", fn.function->name(), gl_error.str());
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void update_viewport(HostState &state) {
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
SDL_GL_GetDrawableSize(state.window.get(), &w, &h);
|
||||
|
||||
state.drawable_size.x = w;
|
||||
state.drawable_size.y = h;
|
||||
|
||||
if (h > 0) {
|
||||
const float window_aspect = static_cast<float>(w) / h;
|
||||
const float vita_aspect = static_cast<float>(DEFAULT_RES_WIDTH) / DEFAULT_RES_HEIGHT;
|
||||
if (window_aspect > vita_aspect) {
|
||||
// Window is wide. Pin top and bottom.
|
||||
state.viewport_size.x = h * vita_aspect;
|
||||
state.viewport_size.y = h;
|
||||
state.viewport_pos.x = (w - state.viewport_size.x) / 2;
|
||||
state.viewport_pos.y = 0;
|
||||
} else {
|
||||
// Window is tall. Pin left and right.
|
||||
state.viewport_size.x = w;
|
||||
state.viewport_size.y = w / vita_aspect;
|
||||
state.viewport_pos.x = 0;
|
||||
state.viewport_pos.y = (h - state.viewport_size.y) / 2;
|
||||
}
|
||||
} else {
|
||||
state.viewport_pos.x = 0;
|
||||
state.viewport_pos.y = 0;
|
||||
state.viewport_size.x = 0;
|
||||
state.viewport_size.y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool init(HostState &state, Config cfg) {
|
||||
const std::unique_ptr<char, void (&)(void *)> base_path(SDL_GetBasePath(), SDL_free);
|
||||
const std::unique_ptr<char, void (&)(void *)> pref_path(SDL_GetPrefPath(org_name, app_name), SDL_free);
|
||||
|
||||
const ResumeAudioThread resume_thread = [&state](SceUID thread_id) {
|
||||
const ThreadStatePtr thread = lock_and_find(thread_id, state.kernel.threads, state.kernel.mutex);
|
||||
const std::lock_guard<std::mutex> lock(thread->mutex);
|
||||
if (thread->to_do == ThreadToDo::wait) {
|
||||
thread->to_do = ThreadToDo::run;
|
||||
}
|
||||
thread->something_to_do.notify_all();
|
||||
};
|
||||
|
||||
state.cfg = std::move(cfg);
|
||||
state.base_path = base_path.get();
|
||||
|
||||
// If configuration already provides preference path
|
||||
if (!state.cfg.pref_path) {
|
||||
state.pref_path = pref_path.get();
|
||||
state.cfg.pref_path = state.pref_path;
|
||||
} else {
|
||||
state.pref_path = state.cfg.pref_path.value();
|
||||
}
|
||||
|
||||
state.window = WindowPtr(SDL_CreateWindow(window_title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, DEFAULT_RES_WIDTH, DEFAULT_RES_HEIGHT, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE), SDL_DestroyWindow);
|
||||
if (!state.window || !init(state.mem) || !init(state.audio, resume_thread) || !init(state.io, state.pref_path.c_str(), state.base_path.c_str())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
|
||||
state.glcontext = GLContextPtr(SDL_GL_CreateContext(state.window.get()), SDL_GL_DeleteContext);
|
||||
if (!state.glcontext) {
|
||||
LOG_ERROR("Could not create OpenGL context.");
|
||||
return false;
|
||||
}
|
||||
|
||||
update_viewport(state);
|
||||
|
||||
// Try adaptive vsync first, falling back to regular vsync.
|
||||
if (SDL_GL_SetSwapInterval(-1) < 0) {
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
}
|
||||
LOG_INFO("Swap interval = {}", SDL_GL_GetSwapInterval());
|
||||
|
||||
const glbinding::GetProcAddress get_proc_address = [](const char *name) {
|
||||
return reinterpret_cast<ProcAddress>(SDL_GL_GetProcAddress(name));
|
||||
};
|
||||
Binding::initialize(get_proc_address, false);
|
||||
Binding::setCallbackMaskExcept(CallbackMask::Before | CallbackMask::After, { "glGetError" });
|
||||
#if MICROPROFILE_ENABLED != 0
|
||||
Binding::setBeforeCallback(before_callback);
|
||||
#endif // MICROPROFILE_ENABLED
|
||||
Binding::setAfterCallback(after_callback);
|
||||
|
||||
state.kernel.base_tick = { rtc_base_ticks() };
|
||||
|
||||
std::string dir_path = state.pref_path + "ux0/app";
|
||||
#ifdef WIN32
|
||||
_WDIR *d = _wopendir((string_utils::utf_to_wide(dir_path)).c_str());
|
||||
_wdirent *dp;
|
||||
#else
|
||||
DIR *d = opendir(dir_path.c_str());
|
||||
dirent *dp;
|
||||
#endif
|
||||
do {
|
||||
std::string d_name_utf8;
|
||||
#ifdef WIN32
|
||||
if ((dp = _wreaddir(d)) != NULL) {
|
||||
d_name_utf8 = string_utils::wide_to_utf(dp->d_name);
|
||||
#else
|
||||
if ((dp = readdir(d)) != NULL) {
|
||||
d_name_utf8 = dp->d_name;
|
||||
#endif
|
||||
if ((strcmp(d_name_utf8.c_str(), ".")) && (strcmp(d_name_utf8.c_str(), ".."))) {
|
||||
vfs::FileBuffer params;
|
||||
state.io.title_id = d_name_utf8;
|
||||
if (vfs::read_app_file(params, state.pref_path, state.io.title_id, "sce_sys/param.sfo")) {
|
||||
SfoFile sfo_handle;
|
||||
load_sfo(sfo_handle, params);
|
||||
find_data(state.game_version, sfo_handle, "APP_VER");
|
||||
find_data(state.game_title, sfo_handle, "TITLE");
|
||||
std::replace(state.game_title.begin(), state.game_title.end(), '\n', ' ');
|
||||
state.gui.game_selector.games.push_back({ state.game_version, state.game_title, state.io.title_id });
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (dp);
|
||||
|
||||
#ifdef WIN32
|
||||
_wclosedir(d);
|
||||
#else
|
||||
closedir(d);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
@ -15,14 +15,13 @@
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
#include "app.h"
|
||||
#include "config.h"
|
||||
#include "screen_render.h"
|
||||
|
||||
#include <gui/functions.h>
|
||||
#include <host/app.h>
|
||||
#include <host/config.h>
|
||||
#include <host/functions.h>
|
||||
#include <host/screen_render.h>
|
||||
#include <host/state.h>
|
||||
#include <host/version.h>
|
||||
#include <kernel/thread/thread_functions.h>
|
||||
#include <shader/spirv_recompiler.h>
|
||||
#include <util/log.h>
|
||||
#include <util/string_utils.h>
|
||||
@ -30,8 +29,6 @@
|
||||
#include <SDL.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
logging::init();
|
||||
|
@ -17,7 +17,8 @@
|
||||
|
||||
#include "SceAppMgrUser.h"
|
||||
|
||||
#include <host/sfo.h>
|
||||
#include <host/functions.h>
|
||||
|
||||
#include <psp2/appmgr.h>
|
||||
|
||||
EXPORT(int, _sceAppMgrGetAppState, void *appState, uint32_t len, uint32_t version) {
|
||||
|
30
src/emulator/performance.cpp
Normal file
30
src/emulator/performance.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
// Vita3K emulator project
|
||||
// Copyright (C) 2018 Vita3K team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
extern "C" {
|
||||
// http://developer.download.nvidia.com/devzone/devcenter/gamegraphics/files/OptimusRenderingPolicies.pdf
|
||||
__declspec(dllexport) uint32_t NvOptimusEnablement = 1;
|
||||
|
||||
// https://gpuopen.com/amdpowerxpressrequesthighperformance/
|
||||
__declspec(dllexport) uint32_t AmdPowerXpressRequestHighPerformance = 1;
|
||||
}
|
||||
|
||||
#endif // _WIN32
|
@ -15,7 +15,8 @@
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
#include <host/screen_render.h>
|
||||
#include "screen_render.h"
|
||||
|
||||
#include <host/state.h>
|
||||
#include <util/log.h>
|
||||
|
78
src/emulator/sfo.cpp
Normal file
78
src/emulator/sfo.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
// Vita3K emulator project
|
||||
// Copyright (C) 2018 Vita3K team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
#include "sfo.h"
|
||||
|
||||
#include <host/sfo.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
bool load_sfo(SfoFile &sfile, const std::vector<uint8_t> &content) {
|
||||
if (content.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(static_cast<void *>(&sfile.header), content.data(), sizeof(SfoHeader));
|
||||
|
||||
sfile.entries.resize(sfile.header.tables_entries + 1);
|
||||
|
||||
for (uint32_t i = 0; i < sfile.header.tables_entries; i++) {
|
||||
memcpy(static_cast<void *>(&sfile.entries[i].entry), content.data() + sizeof(SfoHeader) + i * sizeof(SfoIndexTableEntry), sizeof(SfoIndexTableEntry));
|
||||
}
|
||||
|
||||
sfile.entries[sfile.header.tables_entries].entry.key_offset = sfile.header.data_table_start - sfile.header.key_table_start;
|
||||
|
||||
for (uint32_t i = 0; i < sfile.header.tables_entries; i++) {
|
||||
uint32_t keySize = sfile.entries[i + 1].entry.key_offset - sfile.entries[i].entry.key_offset;
|
||||
|
||||
sfile.entries[i].data.first.resize(keySize);
|
||||
|
||||
memcpy(&sfile.entries[i].data.first[0], &content[sfile.header.key_table_start + sfile.entries[i].entry.key_offset], keySize);
|
||||
|
||||
//Quick hack to remove garbage null terminator caused by reading directly
|
||||
//to buffer
|
||||
sfile.entries[i].data.first = sfile.entries[i].data.first.c_str();
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < sfile.header.tables_entries; i++) {
|
||||
uint32_t dataSize = sfile.entries[i].entry.data_len;
|
||||
|
||||
sfile.entries[i].data.second.resize(dataSize);
|
||||
|
||||
// The last of data is a terminator
|
||||
memcpy(&sfile.entries[i].data.second[0], &content[sfile.header.data_table_start + sfile.entries[i].entry.data_offset], dataSize - 1);
|
||||
|
||||
//Quick hack to remove garbage null terminator caused by reading directly
|
||||
//to buffer
|
||||
sfile.entries[i].data.second = sfile.entries[i].data.second.c_str();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool find_data(std::string &out_data, SfoFile &file, const std::string &key) {
|
||||
auto res = std::find_if(file.entries.begin(), file.entries.end(),
|
||||
[key](auto et) { return et.data.first == key; });
|
||||
|
||||
if (res == file.entries.end()) {
|
||||
return false;
|
||||
}
|
||||
out_data = res->data.second;
|
||||
|
||||
return true;
|
||||
}
|
26
src/emulator/sfo.h
Normal file
26
src/emulator/sfo.h
Normal file
@ -0,0 +1,26 @@
|
||||
// Vita3K emulator project
|
||||
// Copyright (C) 2018 Vita3K team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct SfoFile;
|
||||
|
||||
bool load_sfo(SfoFile &file, const std::vector<uint8_t> &data);
|
||||
bool find_data(std::string &out_data, SfoFile &file, const std::string &key);
|
Loading…
Reference in New Issue
Block a user