mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-25 13:10:14 +00:00
Merge remote-tracking branch 'origin/master' into systime
Implement hourly updates
This commit is contained in:
commit
8c14114d6b
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -7,3 +7,6 @@
|
||||
[submodule "nihstro"]
|
||||
path = externals/nihstro
|
||||
url = https://github.com/neobrain/nihstro.git
|
||||
[submodule "soundtouch"]
|
||||
path = externals/soundtouch
|
||||
url = https://github.com/citra-emu/soundtouch.git
|
||||
|
@ -9,7 +9,7 @@ if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
|
||||
export CXX=g++-5
|
||||
mkdir -p $HOME/.local
|
||||
|
||||
curl -L http://www.cmake.org/files/v2.8/cmake-2.8.11-Linux-i386.tar.gz \
|
||||
curl -L http://www.cmake.org/files/v3.1/cmake-3.1.0-Linux-i386.tar.gz \
|
||||
| tar -xz -C $HOME/.local --strip-components=1
|
||||
|
||||
(
|
||||
@ -20,6 +20,7 @@ if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
|
||||
)
|
||||
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||
brew update > /dev/null # silence the very verbose output
|
||||
brew install qt5 sdl2 dylibbundler
|
||||
brew unlink cmake
|
||||
brew install cmake31 qt5 sdl2 dylibbundler
|
||||
gem install xcpretty
|
||||
fi
|
||||
|
@ -1,6 +1,6 @@
|
||||
# CMake 2.8.11 required for Qt5 settings to be applied automatically on
|
||||
# dependent libraries.
|
||||
cmake_minimum_required(VERSION 2.8.11)
|
||||
# CMake 3.1 required for Qt5 settings to be applied automatically on
|
||||
# dependent libraries and IMPORTED targets.
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
|
||||
function(download_bundled_external remote_path lib_name prefix_var)
|
||||
set(prefix "${CMAKE_BINARY_DIR}/externals/${lib_name}")
|
||||
@ -65,8 +65,8 @@ endif()
|
||||
message(STATUS "Target architecture: ${ARCHITECTURE}")
|
||||
|
||||
if (NOT MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y -Wno-attributes -pthread")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y -Wno-attributes")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
|
||||
|
||||
if (ARCHITECTURE_x86_64)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse4.1")
|
||||
@ -135,6 +135,10 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/externals/cmake-modules")
|
||||
find_package(OpenGL REQUIRED)
|
||||
include_directories(${OPENGL_INCLUDE_DIR})
|
||||
|
||||
# Prefer the -pthread flag on Linux.
|
||||
set (THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
if (ENABLE_SDL2)
|
||||
if (CITRA_USE_BUNDLED_SDL2)
|
||||
# Detect toolchain and platform
|
||||
@ -245,6 +249,9 @@ if(ENABLE_QT)
|
||||
include_directories(externals/qhexedit)
|
||||
add_subdirectory(externals/qhexedit)
|
||||
endif()
|
||||
|
||||
add_subdirectory(externals/soundtouch)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
# Install freedesktop.org metadata files, following those specifications:
|
||||
|
2
externals/boost
vendored
2
externals/boost
vendored
@ -1 +1 @@
|
||||
Subproject commit d81b9269900ae183d0dc98403eea4c971590a807
|
||||
Subproject commit 2dcb9d979665b6aabb1635c617973e02914e60ec
|
7
externals/microprofile/microprofileui.h
vendored
7
externals/microprofile/microprofileui.h
vendored
@ -879,7 +879,7 @@ void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int nBaseY,
|
||||
static int64_t nRefCpu = 0, nRefGpu = 0;
|
||||
if(MicroProfileGetGpuTickReference(&nTickReferenceCpu, &nTickReferenceGpu))
|
||||
{
|
||||
if(0 == nRefCpu || abs(nRefCpu-nBaseTicksCpu) > abs(nTickReferenceCpu-nBaseTicksCpu))
|
||||
if(0 == nRefCpu || std::abs(nRefCpu-nBaseTicksCpu) > std::abs(nTickReferenceCpu-nBaseTicksCpu))
|
||||
{
|
||||
nRefCpu = nTickReferenceCpu;
|
||||
nRefGpu = nTickReferenceGpu;
|
||||
@ -1230,7 +1230,12 @@ void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int nBaseY,
|
||||
char ThreadName[MicroProfileThreadLog::THREAD_MAX_LEN + 16];
|
||||
const char* cLocal = MicroProfileIsLocalThread(nThreadId) ? "*": " ";
|
||||
|
||||
#if defined(WIN32)
|
||||
// nThreadId is 32-bit on Windows
|
||||
int nStrLen = snprintf(ThreadName, sizeof(ThreadName)-1, "%04x: %s%s", nThreadId, cLocal, i < nNumThreadsBase ? &S.Pool[i]->ThreadName[0] : MICROPROFILE_THREAD_NAME_FROM_ID(nThreadId) );
|
||||
#else
|
||||
int nStrLen = snprintf(ThreadName, sizeof(ThreadName)-1, "%04llx: %s%s", nThreadId, cLocal, i < nNumThreadsBase ? &S.Pool[i]->ThreadName[0] : MICROPROFILE_THREAD_NAME_FROM_ID(nThreadId) );
|
||||
#endif
|
||||
uint32_t nThreadColor = -1;
|
||||
if(nThreadId == nContextSwitchHoverThreadAfter || nThreadId == nContextSwitchHoverThreadBefore)
|
||||
nThreadColor = UI.nHoverColorShared|0x906060;
|
||||
|
1
externals/soundtouch
vendored
Submodule
1
externals/soundtouch
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 5274ec4dec498bd88ccbcd28862a0f78a3b95eff
|
@ -16,6 +16,9 @@ set(HEADERS
|
||||
sink.h
|
||||
)
|
||||
|
||||
include_directories(../../externals/soundtouch/include)
|
||||
|
||||
create_directory_groups(${SRCS} ${HEADERS})
|
||||
|
||||
add_library(audio_core STATIC ${SRCS} ${HEADERS})
|
||||
add_library(audio_core STATIC ${SRCS} ${HEADERS})
|
||||
target_link_libraries(audio_core SoundTouch)
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "audio_core/audio_core.h"
|
||||
#include "audio_core/hle/dsp.h"
|
||||
#include "audio_core/hle/pipe.h"
|
||||
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
@ -17,10 +18,10 @@ static constexpr u64 audio_frame_ticks = 1310252ull; ///< Units: ARM11 cycles
|
||||
|
||||
static void AudioTickCallback(u64 /*userdata*/, int cycles_late) {
|
||||
if (DSP::HLE::Tick()) {
|
||||
// HACK: We're not signaling the interrups when they should be, but just firing them all off together.
|
||||
// It should be only (interrupt_id = 2, channel_id = 2) that's signalled here.
|
||||
// TODO(merry): Understand when the other interrupts are fired.
|
||||
DSP_DSP::SignalAllInterrupts();
|
||||
// TODO(merry): Signal all the other interrupts as appropriate.
|
||||
DSP_DSP::SignalPipeInterrupt(DSP::HLE::DspPipe::Audio);
|
||||
// HACK(merry): Added to prevent regressions. Will remove soon.
|
||||
DSP_DSP::SignalPipeInterrupt(DSP::HLE::DspPipe::Binary);
|
||||
}
|
||||
|
||||
// Reschedule recurrent event
|
||||
|
@ -10,8 +10,6 @@ class VMManager;
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
constexpr int num_sources = 24;
|
||||
constexpr int samples_per_frame = 160; ///< Samples per audio frame at native sample rate
|
||||
constexpr int native_sample_rate = 32728; ///< 32kHz
|
||||
|
||||
/// Initialise Audio Core
|
||||
|
@ -7,18 +7,19 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
#include "audio_core/audio_core.h"
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace DSP {
|
||||
namespace HLE {
|
||||
|
||||
constexpr int num_sources = 24;
|
||||
constexpr int samples_per_frame = 160; ///< Samples per audio frame at native sample rate
|
||||
|
||||
/// The final output to the speakers is stereo. Preprocessing output in Source is also stereo.
|
||||
using StereoFrame16 = std::array<std::array<s16, 2>, AudioCore::samples_per_frame>;
|
||||
using StereoFrame16 = std::array<std::array<s16, 2>, samples_per_frame>;
|
||||
|
||||
/// The DSP is quadraphonic internally.
|
||||
using QuadFrame32 = std::array<std::array<s32, 4>, AudioCore::samples_per_frame>;
|
||||
using QuadFrame32 = std::array<std::array<s32, 4>, samples_per_frame>;
|
||||
|
||||
/**
|
||||
* This performs the filter operation defined by FilterT::ProcessSample on the frame in-place.
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
|
||||
#include "audio_core/audio_core.h"
|
||||
#include "audio_core/hle/common.h"
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
@ -305,7 +305,7 @@ struct SourceConfiguration {
|
||||
u16_le buffer_id;
|
||||
};
|
||||
|
||||
Configuration config[AudioCore::num_sources];
|
||||
Configuration config[num_sources];
|
||||
};
|
||||
ASSERT_DSP_STRUCT(SourceConfiguration::Configuration, 192);
|
||||
ASSERT_DSP_STRUCT(SourceConfiguration::Configuration::Buffer, 20);
|
||||
@ -320,7 +320,7 @@ struct SourceStatus {
|
||||
INSERT_PADDING_DSPWORDS(1);
|
||||
};
|
||||
|
||||
Status status[AudioCore::num_sources];
|
||||
Status status[num_sources];
|
||||
};
|
||||
ASSERT_DSP_STRUCT(SourceStatus::Status, 12);
|
||||
|
||||
@ -413,7 +413,7 @@ ASSERT_DSP_STRUCT(DspConfiguration::ReverbEffect, 52);
|
||||
struct AdpcmCoefficients {
|
||||
/// Coefficients are signed fixed point with 11 fractional bits.
|
||||
/// Each source has 16 coefficients associated with it.
|
||||
s16_le coeff[AudioCore::num_sources][16];
|
||||
s16_le coeff[num_sources][16];
|
||||
};
|
||||
ASSERT_DSP_STRUCT(AdpcmCoefficients, 768);
|
||||
|
||||
@ -427,7 +427,7 @@ ASSERT_DSP_STRUCT(DspStatus, 32);
|
||||
/// Final mixed output in PCM16 stereo format, what you hear out of the speakers.
|
||||
/// When the application writes to this region it has no effect.
|
||||
struct FinalMixSamples {
|
||||
s16_le pcm16[2 * AudioCore::samples_per_frame];
|
||||
s16_le pcm16[2 * samples_per_frame];
|
||||
};
|
||||
ASSERT_DSP_STRUCT(FinalMixSamples, 640);
|
||||
|
||||
@ -437,7 +437,7 @@ ASSERT_DSP_STRUCT(FinalMixSamples, 640);
|
||||
/// Values that exceed s16 range will be clipped by the DSP after further processing.
|
||||
struct IntermediateMixSamples {
|
||||
struct Samples {
|
||||
s32_le pcm32[4][AudioCore::samples_per_frame]; ///< Little-endian as opposed to DSP middle-endian.
|
||||
s32_le pcm32[4][samples_per_frame]; ///< Little-endian as opposed to DSP middle-endian.
|
||||
};
|
||||
|
||||
Samples mix1;
|
||||
|
@ -12,12 +12,14 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
#include "core/hle/service/dsp_dsp.h"
|
||||
|
||||
namespace DSP {
|
||||
namespace HLE {
|
||||
|
||||
static DspState dsp_state = DspState::Off;
|
||||
|
||||
static std::array<std::vector<u8>, static_cast<size_t>(DspPipe::DspPipe_MAX)> pipe_data;
|
||||
static std::array<std::vector<u8>, NUM_DSP_PIPE> pipe_data;
|
||||
|
||||
void ResetPipes() {
|
||||
for (auto& data : pipe_data) {
|
||||
@ -27,16 +29,18 @@ void ResetPipes() {
|
||||
}
|
||||
|
||||
std::vector<u8> PipeRead(DspPipe pipe_number, u32 length) {
|
||||
if (pipe_number >= DspPipe::DspPipe_MAX) {
|
||||
LOG_ERROR(Audio_DSP, "pipe_number = %u invalid", pipe_number);
|
||||
const size_t pipe_index = static_cast<size_t>(pipe_number);
|
||||
|
||||
if (pipe_index >= NUM_DSP_PIPE) {
|
||||
LOG_ERROR(Audio_DSP, "pipe_number = %zu invalid", pipe_index);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<u8>& data = pipe_data[static_cast<size_t>(pipe_number)];
|
||||
std::vector<u8>& data = pipe_data[pipe_index];
|
||||
|
||||
if (length > data.size()) {
|
||||
LOG_WARNING(Audio_DSP, "pipe_number = %u is out of data, application requested read of %u but %zu remain",
|
||||
pipe_number, length, data.size());
|
||||
LOG_WARNING(Audio_DSP, "pipe_number = %zu is out of data, application requested read of %u but %zu remain",
|
||||
pipe_index, length, data.size());
|
||||
length = data.size();
|
||||
}
|
||||
|
||||
@ -49,16 +53,20 @@ std::vector<u8> PipeRead(DspPipe pipe_number, u32 length) {
|
||||
}
|
||||
|
||||
size_t GetPipeReadableSize(DspPipe pipe_number) {
|
||||
if (pipe_number >= DspPipe::DspPipe_MAX) {
|
||||
LOG_ERROR(Audio_DSP, "pipe_number = %u invalid", pipe_number);
|
||||
const size_t pipe_index = static_cast<size_t>(pipe_number);
|
||||
|
||||
if (pipe_index >= NUM_DSP_PIPE) {
|
||||
LOG_ERROR(Audio_DSP, "pipe_number = %zu invalid", pipe_index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return pipe_data[static_cast<size_t>(pipe_number)].size();
|
||||
return pipe_data[pipe_index].size();
|
||||
}
|
||||
|
||||
static void WriteU16(DspPipe pipe_number, u16 value) {
|
||||
std::vector<u8>& data = pipe_data[static_cast<size_t>(pipe_number)];
|
||||
const size_t pipe_index = static_cast<size_t>(pipe_number);
|
||||
|
||||
std::vector<u8>& data = pipe_data.at(pipe_index);
|
||||
// Little endian
|
||||
data.emplace_back(value & 0xFF);
|
||||
data.emplace_back(value >> 8);
|
||||
@ -91,6 +99,8 @@ static void AudioPipeWriteStructAddresses() {
|
||||
for (u16 addr : struct_addresses) {
|
||||
WriteU16(DspPipe::Audio, addr);
|
||||
}
|
||||
// Signal that we have data on this pipe.
|
||||
DSP_DSP::SignalPipeInterrupt(DspPipe::Audio);
|
||||
}
|
||||
|
||||
void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) {
|
||||
@ -145,7 +155,7 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) {
|
||||
return;
|
||||
}
|
||||
default:
|
||||
LOG_CRITICAL(Audio_DSP, "pipe_number = %u unimplemented", pipe_number);
|
||||
LOG_CRITICAL(Audio_DSP, "pipe_number = %zu unimplemented", static_cast<size_t>(pipe_number));
|
||||
UNIMPLEMENTED();
|
||||
return;
|
||||
}
|
||||
|
@ -19,9 +19,9 @@ enum class DspPipe {
|
||||
Debug = 0,
|
||||
Dma = 1,
|
||||
Audio = 2,
|
||||
Binary = 3,
|
||||
DspPipe_MAX
|
||||
Binary = 3
|
||||
};
|
||||
constexpr size_t NUM_DSP_PIPE = 8;
|
||||
|
||||
/**
|
||||
* Read a DSP pipe.
|
||||
|
@ -21,7 +21,7 @@ target_link_libraries(citra ${SDL2_LIBRARY} ${OPENGL_gl_LIBRARY} inih glad)
|
||||
if (MSVC)
|
||||
target_link_libraries(citra getopt)
|
||||
endif()
|
||||
target_link_libraries(citra ${PLATFORM_LIBRARIES})
|
||||
target_link_libraries(citra ${PLATFORM_LIBRARIES} Threads::Threads)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|FreeBSD|OpenBSD|NetBSD")
|
||||
install(TARGETS citra RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
// This needs to be included before getopt.h because the latter #defines symbols used by it
|
||||
#include "common/microprofile.h"
|
||||
@ -19,7 +20,7 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/make_unique.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/scope_exit.h"
|
||||
|
||||
#include "core/settings.h"
|
||||
@ -34,26 +35,54 @@
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
|
||||
static void PrintHelp()
|
||||
static void PrintHelp(const char *argv0)
|
||||
{
|
||||
std::cout << "Usage: citra <filename>" << std::endl;
|
||||
std::cout << "Usage: " << argv0 << " [options] <filename>\n"
|
||||
"-g, --gdbport=NUMBER Enable gdb stub on port NUMBER\n"
|
||||
"-h, --help Display this help and exit\n"
|
||||
"-v, --version Output version information and exit\n";
|
||||
}
|
||||
|
||||
static void PrintVersion()
|
||||
{
|
||||
std::cout << "Citra " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl;
|
||||
}
|
||||
|
||||
/// Application entry point
|
||||
int main(int argc, char **argv) {
|
||||
Config config;
|
||||
int option_index = 0;
|
||||
bool use_gdbstub = Settings::values.use_gdbstub;
|
||||
u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port);
|
||||
char *endarg;
|
||||
std::string boot_filename;
|
||||
|
||||
static struct option long_options[] = {
|
||||
{ "gdbport", required_argument, 0, 'g' },
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
{ "version", no_argument, 0, 'v' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
while (optind < argc) {
|
||||
char arg = getopt_long(argc, argv, ":h", long_options, &option_index);
|
||||
char arg = getopt_long(argc, argv, "g:hv", long_options, &option_index);
|
||||
if (arg != -1) {
|
||||
switch (arg) {
|
||||
case 'g':
|
||||
errno = 0;
|
||||
gdb_port = strtoul(optarg, &endarg, 0);
|
||||
use_gdbstub = true;
|
||||
if (endarg == optarg) errno = EINVAL;
|
||||
if (errno != 0) {
|
||||
perror("--gdbport");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
PrintHelp();
|
||||
PrintHelp(argv[0]);
|
||||
return 0;
|
||||
case 'v':
|
||||
PrintVersion();
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
@ -73,16 +102,14 @@ int main(int argc, char **argv) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Config config;
|
||||
log_filter.ParseFilterString(Settings::values.log_filter);
|
||||
|
||||
GDBStub::ToggleServer(Settings::values.use_gdbstub);
|
||||
GDBStub::SetServerPort(static_cast<u32>(Settings::values.gdbstub_port));
|
||||
// Apply the command line arguments
|
||||
Settings::values.gdbstub_port = gdb_port;
|
||||
Settings::values.use_gdbstub = use_gdbstub;
|
||||
Settings::Apply();
|
||||
|
||||
std::unique_ptr<EmuWindow_SDL2> emu_window = Common::make_unique<EmuWindow_SDL2>();
|
||||
|
||||
VideoCore::g_hw_renderer_enabled = Settings::values.use_hw_renderer;
|
||||
VideoCore::g_shader_jit_enabled = Settings::values.use_shader_jit;
|
||||
std::unique_ptr<EmuWindow_SDL2> emu_window = std::make_unique<EmuWindow_SDL2>();
|
||||
|
||||
System::Init(emu_window.get());
|
||||
SCOPE_EXIT({ System::Shutdown(); });
|
||||
|
@ -2,6 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <inih/cpp/INIReader.h>
|
||||
|
||||
#include <SDL.h>
|
||||
@ -10,7 +12,6 @@
|
||||
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/make_unique.h"
|
||||
|
||||
#include "core/settings.h"
|
||||
|
||||
@ -19,7 +20,7 @@
|
||||
Config::Config() {
|
||||
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
|
||||
sdl2_config_loc = FileUtil::GetUserPath(D_CONFIG_IDX) + "sdl2-config.ini";
|
||||
sdl2_config = Common::make_unique<INIReader>(sdl2_config_loc);
|
||||
sdl2_config = std::make_unique<INIReader>(sdl2_config_loc);
|
||||
|
||||
Reload();
|
||||
}
|
||||
@ -31,7 +32,7 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) {
|
||||
LOG_WARNING(Config, "Failed to load %s. Creating file from defaults...", location);
|
||||
FileUtil::CreateFullPath(location);
|
||||
FileUtil::WriteStringToFile(true, default_contents, location);
|
||||
sdl2_config = Common::make_unique<INIReader>(location); // Reopen file
|
||||
sdl2_config = std::make_unique<INIReader>(location); // Reopen file
|
||||
|
||||
return LoadINI(default_contents, false);
|
||||
}
|
||||
@ -64,6 +65,7 @@ void Config::ReadValues() {
|
||||
// Renderer
|
||||
Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", false);
|
||||
Settings::values.use_shader_jit = sdl2_config->GetBoolean("Renderer", "use_shader_jit", true);
|
||||
Settings::values.use_scaled_resolution = sdl2_config->GetBoolean("Renderer", "use_scaled_resolution", false);
|
||||
|
||||
Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 1.0);
|
||||
Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 1.0);
|
||||
|
@ -46,6 +46,10 @@ use_hw_renderer =
|
||||
# 0 : Interpreter (slow), 1 (default): JIT (fast)
|
||||
use_shader_jit =
|
||||
|
||||
# Whether to use native 3DS screen resolution or to scale rendering resolution to the displayed screen size.
|
||||
# 0 (default): Native, 1: Scaled
|
||||
use_scaled_resolution =
|
||||
|
||||
# The clear color for the renderer. What shows up on the sides of the bottom screen.
|
||||
# Must be in range of 0.0-1.0. Defaults to 1.0 for all.
|
||||
bg_red =
|
||||
|
@ -17,12 +17,16 @@ set(SRCS
|
||||
debugger/profiler.cpp
|
||||
debugger/ramview.cpp
|
||||
debugger/registers.cpp
|
||||
game_list.cpp
|
||||
util/spinbox.cpp
|
||||
util/util.cpp
|
||||
bootmanager.cpp
|
||||
configure_debug.cpp
|
||||
configure_dialog.cpp
|
||||
configure_general.cpp
|
||||
game_list.cpp
|
||||
hotkeys.cpp
|
||||
main.cpp
|
||||
ui_settings.cpp
|
||||
citra-qt.rc
|
||||
Info.plist
|
||||
)
|
||||
@ -44,12 +48,16 @@ set(HEADERS
|
||||
debugger/profiler.h
|
||||
debugger/ramview.h
|
||||
debugger/registers.h
|
||||
game_list.h
|
||||
util/spinbox.h
|
||||
util/util.h
|
||||
bootmanager.h
|
||||
configure_debug.h
|
||||
configure_dialog.h
|
||||
configure_general.h
|
||||
game_list.h
|
||||
hotkeys.h
|
||||
main.h
|
||||
ui_settings.h
|
||||
version.h
|
||||
)
|
||||
|
||||
@ -59,6 +67,9 @@ set(UIS
|
||||
debugger/disassembler.ui
|
||||
debugger/profiler.ui
|
||||
debugger/registers.ui
|
||||
configure.ui
|
||||
configure_debug.ui
|
||||
configure_general.ui
|
||||
hotkeys.ui
|
||||
main.ui
|
||||
)
|
||||
@ -81,7 +92,7 @@ else()
|
||||
endif()
|
||||
target_link_libraries(citra-qt core video_core audio_core common qhexedit)
|
||||
target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS})
|
||||
target_link_libraries(citra-qt ${PLATFORM_LIBRARIES})
|
||||
target_link_libraries(citra-qt ${PLATFORM_LIBRARIES} Threads::Threads)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|FreeBSD|OpenBSD|NetBSD")
|
||||
install(TARGETS citra-qt RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
|
||||
|
@ -71,7 +71,9 @@ void EmuThread::run() {
|
||||
// Shutdown the core emulation
|
||||
System::Shutdown();
|
||||
|
||||
#if MICROPROFILE_ENABLED
|
||||
MicroProfileOnThreadExit();
|
||||
#endif
|
||||
|
||||
render_window->moveContext();
|
||||
}
|
||||
|
@ -7,16 +7,16 @@
|
||||
#include <QStringList>
|
||||
|
||||
#include "citra_qt/config.h"
|
||||
#include "citra_qt/ui_settings.h"
|
||||
|
||||
#include "common/file_util.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
Config::Config() {
|
||||
|
||||
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
|
||||
qt_config_loc = FileUtil::GetUserPath(D_CONFIG_IDX) + "qt-config.ini";
|
||||
FileUtil::CreateFullPath(qt_config_loc);
|
||||
qt_config = new QSettings(QString::fromLocal8Bit(qt_config_loc.c_str()), QSettings::IniFormat);
|
||||
qt_config = new QSettings(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
|
||||
|
||||
Reload();
|
||||
}
|
||||
@ -45,6 +45,7 @@ void Config::ReadValues() {
|
||||
qt_config->beginGroup("Renderer");
|
||||
Settings::values.use_hw_renderer = qt_config->value("use_hw_renderer", false).toBool();
|
||||
Settings::values.use_shader_jit = qt_config->value("use_shader_jit", true).toBool();
|
||||
Settings::values.use_scaled_resolution = qt_config->value("use_scaled_resolution", false).toBool();
|
||||
|
||||
Settings::values.bg_red = qt_config->value("bg_red", 1.0).toFloat();
|
||||
Settings::values.bg_green = qt_config->value("bg_green", 1.0).toFloat();
|
||||
@ -67,6 +68,51 @@ void Config::ReadValues() {
|
||||
Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool();
|
||||
Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt();
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("UI");
|
||||
|
||||
qt_config->beginGroup("UILayout");
|
||||
UISettings::values.geometry = qt_config->value("geometry").toByteArray();
|
||||
UISettings::values.state = qt_config->value("state").toByteArray();
|
||||
UISettings::values.renderwindow_geometry = qt_config->value("geometryRenderWindow").toByteArray();
|
||||
UISettings::values.gamelist_header_state = qt_config->value("gameListHeaderState").toByteArray();
|
||||
UISettings::values.microprofile_geometry = qt_config->value("microProfileDialogGeometry").toByteArray();
|
||||
UISettings::values.microprofile_visible = qt_config->value("microProfileDialogVisible", false).toBool();
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Paths");
|
||||
UISettings::values.roms_path = qt_config->value("romsPath").toString();
|
||||
UISettings::values.symbols_path = qt_config->value("symbolsPath").toString();
|
||||
UISettings::values.gamedir = qt_config->value("gameListRootDir", ".").toString();
|
||||
UISettings::values.gamedir_deepscan = qt_config->value("gameListDeepScan", false).toBool();
|
||||
UISettings::values.recent_files = qt_config->value("recentFiles").toStringList();
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Shortcuts");
|
||||
QStringList groups = qt_config->childGroups();
|
||||
for (auto group : groups) {
|
||||
qt_config->beginGroup(group);
|
||||
|
||||
QStringList hotkeys = qt_config->childGroups();
|
||||
for (auto hotkey : hotkeys) {
|
||||
qt_config->beginGroup(hotkey);
|
||||
UISettings::values.shortcuts.emplace_back(
|
||||
UISettings::Shortcut(group + "/" + hotkey,
|
||||
UISettings::ContextualShortcut(qt_config->value("KeySeq").toString(),
|
||||
qt_config->value("Context").toInt())));
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
qt_config->endGroup();
|
||||
|
||||
UISettings::values.single_window_mode = qt_config->value("singleWindowMode", true).toBool();
|
||||
UISettings::values.display_titlebar = qt_config->value("displayTitleBars", true).toBool();
|
||||
UISettings::values.confirm_before_closing = qt_config->value("confirmClose",true).toBool();
|
||||
UISettings::values.first_start = qt_config->value("firstStart", true).toBool();
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
void Config::SaveValues() {
|
||||
@ -84,6 +130,7 @@ void Config::SaveValues() {
|
||||
qt_config->beginGroup("Renderer");
|
||||
qt_config->setValue("use_hw_renderer", Settings::values.use_hw_renderer);
|
||||
qt_config->setValue("use_shader_jit", Settings::values.use_shader_jit);
|
||||
qt_config->setValue("use_scaled_resolution", Settings::values.use_scaled_resolution);
|
||||
|
||||
// Cast to double because Qt's written float values are not human-readable
|
||||
qt_config->setValue("bg_red", (double)Settings::values.bg_red);
|
||||
@ -107,10 +154,44 @@ void Config::SaveValues() {
|
||||
qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub);
|
||||
qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port);
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("UI");
|
||||
|
||||
qt_config->beginGroup("UILayout");
|
||||
qt_config->setValue("geometry", UISettings::values.geometry);
|
||||
qt_config->setValue("state", UISettings::values.state);
|
||||
qt_config->setValue("geometryRenderWindow", UISettings::values.renderwindow_geometry);
|
||||
qt_config->setValue("gameListHeaderState", UISettings::values.gamelist_header_state);
|
||||
qt_config->setValue("microProfileDialogGeometry", UISettings::values.microprofile_geometry);
|
||||
qt_config->setValue("microProfileDialogVisible", UISettings::values.microprofile_visible);
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Paths");
|
||||
qt_config->setValue("romsPath", UISettings::values.roms_path);
|
||||
qt_config->setValue("symbolsPath", UISettings::values.symbols_path);
|
||||
qt_config->setValue("gameListRootDir", UISettings::values.gamedir);
|
||||
qt_config->setValue("gameListDeepScan", UISettings::values.gamedir_deepscan);
|
||||
qt_config->setValue("recentFiles", UISettings::values.recent_files);
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Shortcuts");
|
||||
for (auto shortcut : UISettings::values.shortcuts ) {
|
||||
qt_config->setValue(shortcut.first + "/KeySeq", shortcut.second.first);
|
||||
qt_config->setValue(shortcut.first + "/Context", shortcut.second.second);
|
||||
}
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->setValue("singleWindowMode", UISettings::values.single_window_mode);
|
||||
qt_config->setValue("displayTitleBars", UISettings::values.display_titlebar);
|
||||
qt_config->setValue("confirmClose", UISettings::values.confirm_before_closing);
|
||||
qt_config->setValue("firstStart", UISettings::values.first_start);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
void Config::Reload() {
|
||||
ReadValues();
|
||||
Settings::Apply();
|
||||
}
|
||||
|
||||
void Config::Save() {
|
||||
|
97
src/citra_qt/configure.ui
Normal file
97
src/citra_qt/configure.ui
Normal file
@ -0,0 +1,97 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ConfigureDialog</class>
|
||||
<widget class="QDialog" name="ConfigureDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>441</width>
|
||||
<height>501</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Citra Configuration</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="ConfigureGeneral" name="generalTab">
|
||||
<attribute name="title">
|
||||
<string>General</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="QWidget" name="inputTab">
|
||||
<attribute name="title">
|
||||
<string>Input</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureDebug" name="debugTab">
|
||||
<attribute name="title">
|
||||
<string>Debug</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ConfigureGeneral</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>configure_general.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ConfigureDebug</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>configure_debug.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>ConfigureDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>220</x>
|
||||
<y>380</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>220</x>
|
||||
<y>200</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>ConfigureDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>220</x>
|
||||
<y>380</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>220</x>
|
||||
<y>200</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
31
src/citra_qt/configure_debug.cpp
Normal file
31
src/citra_qt/configure_debug.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "citra_qt/configure_debug.h"
|
||||
#include "ui_configure_debug.h"
|
||||
|
||||
#include "core/settings.h"
|
||||
|
||||
ConfigureDebug::ConfigureDebug(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::ConfigureDebug)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
this->setConfiguration();
|
||||
}
|
||||
|
||||
ConfigureDebug::~ConfigureDebug() {
|
||||
}
|
||||
|
||||
void ConfigureDebug::setConfiguration() {
|
||||
ui->toogle_gdbstub->setChecked(Settings::values.use_gdbstub);
|
||||
ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub);
|
||||
ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port);
|
||||
}
|
||||
|
||||
void ConfigureDebug::applyConfiguration() {
|
||||
Settings::values.use_gdbstub = ui->toogle_gdbstub->isChecked();
|
||||
Settings::values.gdbstub_port = ui->gdbport_spinbox->value();
|
||||
Settings::Apply();
|
||||
}
|
29
src/citra_qt/configure_debug.h
Normal file
29
src/citra_qt/configure_debug.h
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <QWidget>
|
||||
|
||||
namespace Ui {
|
||||
class ConfigureDebug;
|
||||
}
|
||||
|
||||
class ConfigureDebug : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigureDebug(QWidget *parent = nullptr);
|
||||
~ConfigureDebug();
|
||||
|
||||
void applyConfiguration();
|
||||
|
||||
private:
|
||||
void setConfiguration();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::ConfigureDebug> ui;
|
||||
};
|
102
src/citra_qt/configure_debug.ui
Normal file
102
src/citra_qt/configure_debug.ui
Normal file
@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ConfigureDebug</class>
|
||||
<widget class="QWidget" name="ConfigureDebug">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>GDB</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toogle_gdbstub">
|
||||
<property name="text">
|
||||
<string>Enable GDB Stub</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Port:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="gdbport_spinbox">
|
||||
<property name="maximum">
|
||||
<number>65536</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>toogle_gdbstub</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>gdbport_spinbox</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>84</x>
|
||||
<y>157</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>342</x>
|
||||
<y>158</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
29
src/citra_qt/configure_dialog.cpp
Normal file
29
src/citra_qt/configure_dialog.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "citra_qt/config.h"
|
||||
#include "citra_qt/configure_dialog.h"
|
||||
#include "ui_configure.h"
|
||||
|
||||
|
||||
#include "core/settings.h"
|
||||
|
||||
ConfigureDialog::ConfigureDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::ConfigureDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
this->setConfiguration();
|
||||
}
|
||||
|
||||
ConfigureDialog::~ConfigureDialog() {
|
||||
}
|
||||
|
||||
void ConfigureDialog::setConfiguration() {
|
||||
}
|
||||
|
||||
void ConfigureDialog::applyConfiguration() {
|
||||
ui->generalTab->applyConfiguration();
|
||||
ui->debugTab->applyConfiguration();
|
||||
}
|
29
src/citra_qt/configure_dialog.h
Normal file
29
src/citra_qt/configure_dialog.h
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class ConfigureDialog;
|
||||
}
|
||||
|
||||
class ConfigureDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigureDialog(QWidget *parent = nullptr);
|
||||
~ConfigureDialog();
|
||||
|
||||
void applyConfiguration();
|
||||
|
||||
private:
|
||||
void setConfiguration();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::ConfigureDialog> ui;
|
||||
};
|
39
src/citra_qt/configure_general.cpp
Normal file
39
src/citra_qt/configure_general.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "citra_qt/configure_general.h"
|
||||
#include "citra_qt/ui_settings.h"
|
||||
#include "ui_configure_general.h"
|
||||
|
||||
#include "core/settings.h"
|
||||
|
||||
ConfigureGeneral::ConfigureGeneral(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::ConfigureGeneral)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
this->setConfiguration();
|
||||
}
|
||||
|
||||
ConfigureGeneral::~ConfigureGeneral() {
|
||||
}
|
||||
|
||||
void ConfigureGeneral::setConfiguration() {
|
||||
ui->toogle_deepscan->setChecked(UISettings::values.gamedir_deepscan);
|
||||
ui->toogle_check_exit->setChecked(UISettings::values.confirm_before_closing);
|
||||
ui->region_combobox->setCurrentIndex(Settings::values.region_value);
|
||||
ui->toogle_hw_renderer->setChecked(Settings::values.use_hw_renderer);
|
||||
ui->toogle_shader_jit->setChecked(Settings::values.use_shader_jit);
|
||||
ui->toogle_scaled_resolution->setChecked(Settings::values.use_scaled_resolution);
|
||||
}
|
||||
|
||||
void ConfigureGeneral::applyConfiguration() {
|
||||
UISettings::values.gamedir_deepscan = ui->toogle_deepscan->isChecked();
|
||||
UISettings::values.confirm_before_closing = ui->toogle_check_exit->isChecked();
|
||||
Settings::values.region_value = ui->region_combobox->currentIndex();
|
||||
Settings::values.use_hw_renderer = ui->toogle_hw_renderer->isChecked();
|
||||
Settings::values.use_shader_jit = ui->toogle_shader_jit->isChecked();
|
||||
Settings::values.use_scaled_resolution = ui->toogle_scaled_resolution->isChecked();
|
||||
Settings::Apply();
|
||||
}
|
29
src/citra_qt/configure_general.h
Normal file
29
src/citra_qt/configure_general.h
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <QWidget>
|
||||
|
||||
namespace Ui {
|
||||
class ConfigureGeneral;
|
||||
}
|
||||
|
||||
class ConfigureGeneral : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigureGeneral(QWidget *parent = nullptr);
|
||||
~ConfigureGeneral();
|
||||
|
||||
void applyConfiguration();
|
||||
|
||||
private:
|
||||
void setConfiguration();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::ConfigureGeneral> ui;
|
||||
};
|
173
src/citra_qt/configure_general.ui
Normal file
173
src/citra_qt/configure_general.ui
Normal file
@ -0,0 +1,173 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ConfigureGeneral</class>
|
||||
<widget class="QWidget" name="ConfigureGeneral">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>300</width>
|
||||
<height>377</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>General</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toogle_deepscan">
|
||||
<property name="text">
|
||||
<string>Recursive scan for game folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toogle_check_exit">
|
||||
<property name="text">
|
||||
<string>Confirm exit while emulation is running</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_4">
|
||||
<property name="title">
|
||||
<string>Emulation</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Region:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="region_combobox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">JPN</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">USA</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">EUR</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">AUS</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">CHN</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">KOR</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">TWN</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>Performance</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toogle_hw_renderer">
|
||||
<property name="text">
|
||||
<string>Enable hardware renderer</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toogle_shader_jit">
|
||||
<property name="text">
|
||||
<string>Enable shader JIT</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toogle_scaled_resolution">
|
||||
<property name="text">
|
||||
<string>Enable scaled resolution</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3">
|
||||
<property name="title">
|
||||
<string>Hotkeys</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="GHotkeysDialog" name="widget" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>GHotkeysDialog</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>hotkeys.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -75,7 +75,7 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const
|
||||
case Role_IsEnabled:
|
||||
{
|
||||
auto context = context_weak.lock();
|
||||
return context && context->breakpoints[event].enabled;
|
||||
return context && context->breakpoints[(int)event].enabled;
|
||||
}
|
||||
|
||||
default:
|
||||
@ -110,7 +110,7 @@ bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, i
|
||||
if (!context)
|
||||
return false;
|
||||
|
||||
context->breakpoints[event].enabled = value == Qt::Checked;
|
||||
context->breakpoints[(int)event].enabled = value == Qt::Checked;
|
||||
QModelIndex changed_index = createIndex(index.row(), 0);
|
||||
emit dataChanged(changed_index, changed_index);
|
||||
return true;
|
||||
|
@ -346,5 +346,11 @@ u32 GraphicsFramebufferWidget::BytesPerPixel(GraphicsFramebufferWidget::Format f
|
||||
case Format::RGBA4:
|
||||
case Format::D16:
|
||||
return 2;
|
||||
default:
|
||||
UNREACHABLE_MSG("GraphicsFramebufferWidget::BytesPerPixel: this "
|
||||
"should not be reached as this function should "
|
||||
"be given a format which is in "
|
||||
"GraphicsFramebufferWidget::Format. Instead got %i",
|
||||
static_cast<int>(format));
|
||||
}
|
||||
}
|
||||
|
@ -9,13 +9,16 @@
|
||||
#include "citra_qt/debugger/profiler.h"
|
||||
#include "citra_qt/util/util.h"
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/profiler_reporting.h"
|
||||
|
||||
// Include the implementation of the UI in this file. This isn't in microprofile.cpp because the
|
||||
// non-Qt frontends don't need it (and don't implement the UI drawing hooks either).
|
||||
#if MICROPROFILE_ENABLED
|
||||
#define MICROPROFILEUI_IMPL 1
|
||||
#include "common/microprofileui.h"
|
||||
#endif
|
||||
|
||||
using namespace Common::Profiling;
|
||||
|
||||
@ -34,21 +37,9 @@ static QVariant GetDataForColumn(int col, const AggregatedDuration& duration)
|
||||
}
|
||||
}
|
||||
|
||||
static const TimingCategoryInfo* GetCategoryInfo(int id)
|
||||
{
|
||||
const auto& categories = GetProfilingManager().GetTimingCategoriesInfo();
|
||||
if ((size_t)id >= categories.size()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return &categories[id];
|
||||
}
|
||||
}
|
||||
|
||||
ProfilerModel::ProfilerModel(QObject* parent) : QAbstractItemModel(parent)
|
||||
{
|
||||
updateProfilingInfo();
|
||||
const auto& categories = GetProfilingManager().GetTimingCategoriesInfo();
|
||||
results.time_per_category.resize(categories.size());
|
||||
}
|
||||
|
||||
QVariant ProfilerModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
@ -85,7 +76,7 @@ int ProfilerModel::rowCount(const QModelIndex& parent) const
|
||||
if (parent.isValid()) {
|
||||
return 0;
|
||||
} else {
|
||||
return static_cast<int>(results.time_per_category.size() + 2);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,17 +95,6 @@ QVariant ProfilerModel::data(const QModelIndex& index, int role) const
|
||||
} else {
|
||||
return GetDataForColumn(index.column(), results.interframe_time);
|
||||
}
|
||||
} else {
|
||||
if (index.column() == 0) {
|
||||
const TimingCategoryInfo* info = GetCategoryInfo(index.row() - 2);
|
||||
return info != nullptr ? QString(info->name) : QVariant();
|
||||
} else {
|
||||
if (index.row() - 2 < (int)results.time_per_category.size()) {
|
||||
return GetDataForColumn(index.column(), results.time_per_category[index.row() - 2]);
|
||||
} else {
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,6 +128,8 @@ void ProfilerWidget::setProfilingInfoUpdateEnabled(bool enable)
|
||||
}
|
||||
}
|
||||
|
||||
#if MICROPROFILE_ENABLED
|
||||
|
||||
class MicroProfileWidget : public QWidget {
|
||||
public:
|
||||
MicroProfileWidget(QWidget* parent = nullptr);
|
||||
@ -171,6 +153,8 @@ private:
|
||||
QTimer update_timer;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
MicroProfileDialog::MicroProfileDialog(QWidget* parent)
|
||||
: QWidget(parent, Qt::Dialog)
|
||||
{
|
||||
@ -180,6 +164,8 @@ MicroProfileDialog::MicroProfileDialog(QWidget* parent)
|
||||
// Remove the "?" button from the titlebar and enable the maximize button
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint | Qt::WindowMaximizeButtonHint);
|
||||
|
||||
#if MICROPROFILE_ENABLED
|
||||
|
||||
MicroProfileWidget* widget = new MicroProfileWidget(this);
|
||||
|
||||
QLayout* layout = new QVBoxLayout(this);
|
||||
@ -191,6 +177,7 @@ MicroProfileDialog::MicroProfileDialog(QWidget* parent)
|
||||
setFocusProxy(widget);
|
||||
widget->setFocusPolicy(Qt::StrongFocus);
|
||||
widget->setFocus();
|
||||
#endif
|
||||
}
|
||||
|
||||
QAction* MicroProfileDialog::toggleViewAction() {
|
||||
@ -218,6 +205,9 @@ void MicroProfileDialog::hideEvent(QHideEvent* ev) {
|
||||
QWidget::hideEvent(ev);
|
||||
}
|
||||
|
||||
|
||||
#if MICROPROFILE_ENABLED
|
||||
|
||||
/// There's no way to pass a user pointer to MicroProfile, so this variable is used to make the
|
||||
/// QPainter available inside the drawing callbacks.
|
||||
static QPainter* mp_painter = nullptr;
|
||||
@ -337,3 +327,4 @@ void MicroProfileDrawLine2D(u32 vertices_length, float* vertices, u32 hex_color)
|
||||
mp_painter->drawPolyline(point_buf.data(), vertices_length);
|
||||
point_buf.clear();
|
||||
}
|
||||
#endif
|
||||
|
@ -7,8 +7,10 @@
|
||||
#include <QAbstractItemModel>
|
||||
#include <QDockWidget>
|
||||
#include <QTimer>
|
||||
|
||||
#include "ui_profiler.h"
|
||||
|
||||
#include "common/microprofile.h"
|
||||
#include "common/profiler_reporting.h"
|
||||
|
||||
class ProfilerModel : public QAbstractItemModel
|
||||
@ -49,6 +51,7 @@ private:
|
||||
QTimer update_timer;
|
||||
};
|
||||
|
||||
|
||||
class MicroProfileDialog : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "game_list.h"
|
||||
#include "game_list_p.h"
|
||||
#include "ui_settings.h"
|
||||
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
@ -66,7 +67,7 @@ void GameList::ValidateEntry(const QModelIndex& item)
|
||||
|
||||
if (file_path.isEmpty())
|
||||
return;
|
||||
std::string std_file_path(file_path.toLocal8Bit());
|
||||
std::string std_file_path(file_path.toStdString());
|
||||
if (!FileUtil::Exists(std_file_path) || FileUtil::IsDirectory(std_file_path))
|
||||
return;
|
||||
emit GameChosen(file_path);
|
||||
@ -100,19 +101,15 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan)
|
||||
current_worker = std::move(worker);
|
||||
}
|
||||
|
||||
void GameList::SaveInterfaceLayout(QSettings& settings)
|
||||
void GameList::SaveInterfaceLayout()
|
||||
{
|
||||
settings.beginGroup("UILayout");
|
||||
settings.setValue("gameListHeaderState", tree_view->header()->saveState());
|
||||
settings.endGroup();
|
||||
UISettings::values.gamelist_header_state = tree_view->header()->saveState();
|
||||
}
|
||||
|
||||
void GameList::LoadInterfaceLayout(QSettings& settings)
|
||||
void GameList::LoadInterfaceLayout()
|
||||
{
|
||||
auto header = tree_view->header();
|
||||
settings.beginGroup("UILayout");
|
||||
header->restoreState(settings.value("gameListHeaderState").toByteArray());
|
||||
settings.endGroup();
|
||||
header->restoreState(UISettings::values.gamelist_header_state);
|
||||
|
||||
item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder());
|
||||
}
|
||||
@ -148,7 +145,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, bool d
|
||||
|
||||
emit EntryReady({
|
||||
new GameListItem(QString::fromStdString(Loader::GetFileTypeString(filetype))),
|
||||
new GameListItemPath(QString::fromLocal8Bit(physical_name.c_str())),
|
||||
new GameListItemPath(QString::fromStdString(physical_name)),
|
||||
new GameListItemSize(FileUtil::GetSize(physical_name)),
|
||||
});
|
||||
}
|
||||
|
@ -31,8 +31,8 @@ public:
|
||||
|
||||
void PopulateAsync(const QString& dir_path, bool deep_scan);
|
||||
|
||||
void SaveInterfaceLayout(QSettings& settings);
|
||||
void LoadInterfaceLayout(QSettings& settings);
|
||||
void SaveInterfaceLayout();
|
||||
void LoadInterfaceLayout();
|
||||
|
||||
public slots:
|
||||
void AddEntry(QList<QStandardItem*> entry_items);
|
||||
|
@ -4,11 +4,12 @@
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QKeySequence>
|
||||
#include <QSettings>
|
||||
#include <QShortcut>
|
||||
|
||||
#include "citra_qt/hotkeys.h"
|
||||
#include "citra_qt/ui_settings.h"
|
||||
|
||||
struct Hotkey
|
||||
{
|
||||
@ -24,54 +25,39 @@ typedef std::map<QString, HotkeyMap> HotkeyGroupMap;
|
||||
|
||||
HotkeyGroupMap hotkey_groups;
|
||||
|
||||
void SaveHotkeys(QSettings& settings)
|
||||
void SaveHotkeys()
|
||||
{
|
||||
settings.beginGroup("Shortcuts");
|
||||
|
||||
UISettings::values.shortcuts.clear();
|
||||
for (auto group : hotkey_groups)
|
||||
{
|
||||
settings.beginGroup(group.first);
|
||||
for (auto hotkey : group.second)
|
||||
{
|
||||
settings.beginGroup(hotkey.first);
|
||||
settings.setValue(QString("KeySeq"), hotkey.second.keyseq.toString());
|
||||
settings.setValue(QString("Context"), hotkey.second.context);
|
||||
settings.endGroup();
|
||||
UISettings::values.shortcuts.emplace_back(
|
||||
UISettings::Shortcut(group.first + "/" + hotkey.first,
|
||||
UISettings::ContextualShortcut(hotkey.second.keyseq.toString(),
|
||||
hotkey.second.context)));
|
||||
}
|
||||
settings.endGroup();
|
||||
}
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
void LoadHotkeys(QSettings& settings)
|
||||
void LoadHotkeys()
|
||||
{
|
||||
settings.beginGroup("Shortcuts");
|
||||
|
||||
// Make sure NOT to use a reference here because it would become invalid once we call beginGroup()
|
||||
QStringList groups = settings.childGroups();
|
||||
for (auto group : groups)
|
||||
for (auto shortcut : UISettings::values.shortcuts)
|
||||
{
|
||||
settings.beginGroup(group);
|
||||
QStringList cat = shortcut.first.split("/");
|
||||
Q_ASSERT(cat.size() >= 2);
|
||||
|
||||
QStringList hotkeys = settings.childGroups();
|
||||
for (auto hotkey : hotkeys)
|
||||
// RegisterHotkey assigns default keybindings, so use old values as default parameters
|
||||
Hotkey& hk = hotkey_groups[cat[0]][cat[1]];
|
||||
if (!shortcut.second.first.isEmpty())
|
||||
{
|
||||
settings.beginGroup(hotkey);
|
||||
|
||||
// RegisterHotkey assigns default keybindings, so use old values as default parameters
|
||||
Hotkey& hk = hotkey_groups[group][hotkey];
|
||||
hk.keyseq = QKeySequence::fromString(settings.value("KeySeq", hk.keyseq.toString()).toString());
|
||||
hk.context = (Qt::ShortcutContext)settings.value("Context", hk.context).toInt();
|
||||
if (hk.shortcut)
|
||||
hk.shortcut->setKey(hk.keyseq);
|
||||
|
||||
settings.endGroup();
|
||||
hk.keyseq = QKeySequence::fromString(shortcut.second.first);
|
||||
hk.context = (Qt::ShortcutContext)shortcut.second.second;
|
||||
}
|
||||
|
||||
settings.endGroup();
|
||||
if (hk.shortcut)
|
||||
hk.shortcut->setKey(hk.keyseq);
|
||||
}
|
||||
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
void RegisterHotkey(const QString& group, const QString& action, const QKeySequence& default_keyseq, Qt::ShortcutContext default_context)
|
||||
@ -94,7 +80,7 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge
|
||||
}
|
||||
|
||||
|
||||
GHotkeysDialog::GHotkeysDialog(QWidget* parent): QDialog(parent)
|
||||
GHotkeysDialog::GHotkeysDialog(QWidget* parent): QWidget(parent)
|
||||
{
|
||||
ui.setupUi(this);
|
||||
|
||||
|
@ -2,6 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ui_hotkeys.h"
|
||||
|
||||
class QDialog;
|
||||
@ -33,16 +35,16 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge
|
||||
*
|
||||
* @note Each hotkey group will be stored a settings group; For each hotkey inside that group, a settings group will be created to store the key sequence and the hotkey context.
|
||||
*/
|
||||
void SaveHotkeys(QSettings& settings);
|
||||
void SaveHotkeys();
|
||||
|
||||
/**
|
||||
* Loads hotkeys from the settings file.
|
||||
*
|
||||
* @note Yet unregistered hotkeys which are present in the settings will automatically be registered.
|
||||
*/
|
||||
void LoadHotkeys(QSettings& settings);
|
||||
void LoadHotkeys();
|
||||
|
||||
class GHotkeysDialog : public QDialog
|
||||
class GHotkeysDialog : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>hotkeys</class>
|
||||
<widget class="QDialog" name="hotkeys">
|
||||
<widget class="QWidget" name="hotkeys">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
@ -39,51 +39,8 @@
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>hotkeys</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>hotkeys</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <clocale>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include <QDesktopWidget>
|
||||
@ -13,9 +14,11 @@
|
||||
|
||||
#include "citra_qt/bootmanager.h"
|
||||
#include "citra_qt/config.h"
|
||||
#include "citra_qt/configure_dialog.h"
|
||||
#include "citra_qt/game_list.h"
|
||||
#include "citra_qt/hotkeys.h"
|
||||
#include "citra_qt/main.h"
|
||||
#include "citra_qt/ui_settings.h"
|
||||
|
||||
// Debugger
|
||||
#include "citra_qt/debugger/callstack.h"
|
||||
@ -30,7 +33,6 @@
|
||||
#include "citra_qt/debugger/ramview.h"
|
||||
#include "citra_qt/debugger/registers.h"
|
||||
|
||||
#include "common/make_unique.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/platform.h"
|
||||
#include "common/scm_rev.h"
|
||||
@ -50,12 +52,10 @@
|
||||
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
GMainWindow::GMainWindow() : emu_thread(nullptr)
|
||||
GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
|
||||
{
|
||||
Pica::g_debug_context = Pica::DebugContext::Construct();
|
||||
|
||||
Config config;
|
||||
|
||||
ui.setupUi(this);
|
||||
statusBar()->hide();
|
||||
|
||||
@ -69,8 +69,10 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
|
||||
addDockWidget(Qt::BottomDockWidgetArea, profilerWidget);
|
||||
profilerWidget->hide();
|
||||
|
||||
#if MICROPROFILE_ENABLED
|
||||
microProfileDialog = new MicroProfileDialog(this);
|
||||
microProfileDialog->hide();
|
||||
#endif
|
||||
|
||||
disasmWidget = new DisassemblerWidget(this, emu_thread.get());
|
||||
addDockWidget(Qt::BottomDockWidgetArea, disasmWidget);
|
||||
@ -110,7 +112,9 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
|
||||
|
||||
QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging"));
|
||||
debug_menu->addAction(profilerWidget->toggleViewAction());
|
||||
#if MICROPROFILE_ENABLED
|
||||
debug_menu->addAction(microProfileDialog->toggleViewAction());
|
||||
#endif
|
||||
debug_menu->addAction(disasmWidget->toggleViewAction());
|
||||
debug_menu->addAction(registersWidget->toggleViewAction());
|
||||
debug_menu->addAction(callstackWidget->toggleViewAction());
|
||||
@ -133,33 +137,20 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
|
||||
setGeometry(x, y, w, h);
|
||||
|
||||
// Restore UI state
|
||||
QSettings settings;
|
||||
restoreGeometry(UISettings::values.geometry);
|
||||
restoreState(UISettings::values.state);
|
||||
render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
|
||||
#if MICROPROFILE_ENABLED
|
||||
microProfileDialog->restoreGeometry(UISettings::values.microprofile_geometry);
|
||||
microProfileDialog->setVisible(UISettings::values.microprofile_visible);
|
||||
#endif
|
||||
|
||||
settings.beginGroup("UILayout");
|
||||
restoreGeometry(settings.value("geometry").toByteArray());
|
||||
restoreState(settings.value("state").toByteArray());
|
||||
render_window->restoreGeometry(settings.value("geometryRenderWindow").toByteArray());
|
||||
microProfileDialog->restoreGeometry(settings.value("microProfileDialogGeometry").toByteArray());
|
||||
microProfileDialog->setVisible(settings.value("microProfileDialogVisible").toBool());
|
||||
settings.endGroup();
|
||||
game_list->LoadInterfaceLayout();
|
||||
|
||||
game_list->LoadInterfaceLayout(settings);
|
||||
|
||||
ui.action_Use_Gdbstub->setChecked(Settings::values.use_gdbstub);
|
||||
SetGdbstubEnabled(ui.action_Use_Gdbstub->isChecked());
|
||||
|
||||
GDBStub::SetServerPort(static_cast<u32>(Settings::values.gdbstub_port));
|
||||
|
||||
ui.action_Use_Hardware_Renderer->setChecked(Settings::values.use_hw_renderer);
|
||||
SetHardwareRendererEnabled(ui.action_Use_Hardware_Renderer->isChecked());
|
||||
|
||||
ui.action_Use_Shader_JIT->setChecked(Settings::values.use_shader_jit);
|
||||
SetShaderJITEnabled(ui.action_Use_Shader_JIT->isChecked());
|
||||
|
||||
ui.action_Single_Window_Mode->setChecked(settings.value("singleWindowMode", true).toBool());
|
||||
ui.action_Single_Window_Mode->setChecked(UISettings::values.single_window_mode);
|
||||
ToggleWindowMode();
|
||||
|
||||
ui.actionDisplay_widget_title_bars->setChecked(settings.value("displayTitleBars", true).toBool());
|
||||
ui.actionDisplay_widget_title_bars->setChecked(UISettings::values.display_titlebar);
|
||||
OnDisplayTitleBars(ui.actionDisplay_widget_title_bars->isChecked());
|
||||
|
||||
// Prepare actions for recent files
|
||||
@ -172,21 +163,16 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
|
||||
}
|
||||
UpdateRecentFiles();
|
||||
|
||||
confirm_before_closing = settings.value("confirmClose", true).toBool();
|
||||
|
||||
// Setup connections
|
||||
connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString)));
|
||||
connect(ui.action_Load_File, SIGNAL(triggered()), this, SLOT(OnMenuLoadFile()));
|
||||
connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString)), Qt::DirectConnection);
|
||||
connect(ui.action_Configure, SIGNAL(triggered()), this, SLOT(OnConfigure()));
|
||||
connect(ui.action_Load_File, SIGNAL(triggered()), this, SLOT(OnMenuLoadFile()),Qt::DirectConnection);
|
||||
connect(ui.action_Load_Symbol_Map, SIGNAL(triggered()), this, SLOT(OnMenuLoadSymbolMap()));
|
||||
connect(ui.action_Select_Game_List_Root, SIGNAL(triggered()), this, SLOT(OnMenuSelectGameListRoot()));
|
||||
connect(ui.action_Start, SIGNAL(triggered()), this, SLOT(OnStartGame()));
|
||||
connect(ui.action_Pause, SIGNAL(triggered()), this, SLOT(OnPauseGame()));
|
||||
connect(ui.action_Stop, SIGNAL(triggered()), this, SLOT(OnStopGame()));
|
||||
connect(ui.action_Use_Hardware_Renderer, SIGNAL(triggered(bool)), this, SLOT(SetHardwareRendererEnabled(bool)));
|
||||
connect(ui.action_Use_Shader_JIT, SIGNAL(triggered(bool)), this, SLOT(SetShaderJITEnabled(bool)));
|
||||
connect(ui.action_Use_Gdbstub, SIGNAL(triggered(bool)), this, SLOT(SetGdbstubEnabled(bool)));
|
||||
connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode()));
|
||||
connect(ui.action_Hotkeys, SIGNAL(triggered()), this, SLOT(OnOpenHotkeysDialog()));
|
||||
|
||||
connect(this, SIGNAL(EmulationStarting(EmuThread*)), disasmWidget, SLOT(OnEmulationStarting(EmuThread*)));
|
||||
connect(this, SIGNAL(EmulationStopping()), disasmWidget, SLOT(OnEmulationStopping()));
|
||||
@ -201,7 +187,7 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
|
||||
// Setup hotkeys
|
||||
RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
|
||||
RegisterHotkey("Main Window", "Start Emulation");
|
||||
LoadHotkeys(settings);
|
||||
LoadHotkeys();
|
||||
|
||||
connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this, SLOT(OnMenuLoadFile()));
|
||||
connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, SLOT(OnStartGame()));
|
||||
@ -211,7 +197,7 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
|
||||
|
||||
show();
|
||||
|
||||
game_list->PopulateAsync(settings.value("gameListRootDir", ".").toString(), settings.value("gameListDeepScan", false).toBool());
|
||||
game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
|
||||
|
||||
QStringList args = QApplication::arguments();
|
||||
if (args.length() >= 2) {
|
||||
@ -319,7 +305,7 @@ void GMainWindow::BootGame(const std::string& filename) {
|
||||
return;
|
||||
|
||||
// Create and start the emulation thread
|
||||
emu_thread = Common::make_unique<EmuThread>(render_window);
|
||||
emu_thread = std::make_unique<EmuThread>(render_window);
|
||||
emit EmulationStarting(emu_thread.get());
|
||||
render_window->moveContext();
|
||||
emu_thread->start();
|
||||
@ -375,32 +361,24 @@ void GMainWindow::ShutdownGame() {
|
||||
emulation_running = false;
|
||||
}
|
||||
|
||||
void GMainWindow::StoreRecentFile(const std::string& filename)
|
||||
{
|
||||
QSettings settings;
|
||||
QStringList recent_files = settings.value("recentFiles").toStringList();
|
||||
recent_files.prepend(QString::fromStdString(filename));
|
||||
recent_files.removeDuplicates();
|
||||
while (recent_files.size() > max_recent_files_item) {
|
||||
recent_files.removeLast();
|
||||
void GMainWindow::StoreRecentFile(const std::string& filename) {
|
||||
UISettings::values.recent_files.prepend(QString::fromStdString(filename));
|
||||
UISettings::values.recent_files.removeDuplicates();
|
||||
while (UISettings::values.recent_files.size() > max_recent_files_item) {
|
||||
UISettings::values.recent_files.removeLast();
|
||||
}
|
||||
|
||||
settings.setValue("recentFiles", recent_files);
|
||||
|
||||
UpdateRecentFiles();
|
||||
}
|
||||
|
||||
void GMainWindow::UpdateRecentFiles() {
|
||||
QSettings settings;
|
||||
QStringList recent_files = settings.value("recentFiles").toStringList();
|
||||
|
||||
unsigned int num_recent_files = std::min(recent_files.size(), static_cast<int>(max_recent_files_item));
|
||||
unsigned int num_recent_files = std::min(UISettings::values.recent_files.size(), static_cast<int>(max_recent_files_item));
|
||||
|
||||
for (unsigned int i = 0; i < num_recent_files; i++) {
|
||||
QString text = QString("&%1. %2").arg(i + 1).arg(QFileInfo(recent_files[i]).fileName());
|
||||
QString text = QString("&%1. %2").arg(i + 1).arg(QFileInfo(UISettings::values.recent_files[i]).fileName());
|
||||
actions_recent_files[i]->setText(text);
|
||||
actions_recent_files[i]->setData(recent_files[i]);
|
||||
actions_recent_files[i]->setToolTip(recent_files[i]);
|
||||
actions_recent_files[i]->setData(UISettings::values.recent_files[i]);
|
||||
actions_recent_files[i]->setToolTip(UISettings::values.recent_files[i]);
|
||||
actions_recent_files[i]->setVisible(true);
|
||||
}
|
||||
|
||||
@ -417,40 +395,32 @@ void GMainWindow::UpdateRecentFiles() {
|
||||
}
|
||||
|
||||
void GMainWindow::OnGameListLoadFile(QString game_path) {
|
||||
BootGame(game_path.toLocal8Bit().data());
|
||||
BootGame(game_path.toStdString());
|
||||
}
|
||||
|
||||
void GMainWindow::OnMenuLoadFile() {
|
||||
QSettings settings;
|
||||
QString rom_path = settings.value("romsPath", QString()).toString();
|
||||
|
||||
QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), rom_path, tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.cci *.cxi)"));
|
||||
QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), UISettings::values.roms_path, tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.cci *.cxi)"));
|
||||
if (!filename.isEmpty()) {
|
||||
settings.setValue("romsPath", QFileInfo(filename).path());
|
||||
UISettings::values.roms_path = QFileInfo(filename).path();
|
||||
|
||||
BootGame(filename.toLocal8Bit().data());
|
||||
BootGame(filename.toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnMenuLoadSymbolMap() {
|
||||
QSettings settings;
|
||||
QString symbol_path = settings.value("symbolsPath", QString()).toString();
|
||||
|
||||
QString filename = QFileDialog::getOpenFileName(this, tr("Load Symbol Map"), symbol_path, tr("Symbol map (*)"));
|
||||
QString filename = QFileDialog::getOpenFileName(this, tr("Load Symbol Map"), UISettings::values.symbols_path, tr("Symbol map (*)"));
|
||||
if (!filename.isEmpty()) {
|
||||
settings.setValue("symbolsPath", QFileInfo(filename).path());
|
||||
UISettings::values.symbols_path = QFileInfo(filename).path();
|
||||
|
||||
LoadSymbolMap(filename.toLocal8Bit().data());
|
||||
LoadSymbolMap(filename.toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnMenuSelectGameListRoot() {
|
||||
QSettings settings;
|
||||
|
||||
QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory"));
|
||||
if (!dir_path.isEmpty()) {
|
||||
settings.setValue("gameListRootDir", dir_path);
|
||||
game_list->PopulateAsync(dir_path, settings.value("gameListDeepScan").toBool());
|
||||
UISettings::values.gamedir = dir_path;
|
||||
game_list->PopulateAsync(dir_path, UISettings::values.gamedir_deepscan);
|
||||
}
|
||||
}
|
||||
|
||||
@ -461,15 +431,12 @@ void GMainWindow::OnMenuRecentFile() {
|
||||
QString filename = action->data().toString();
|
||||
QFileInfo file_info(filename);
|
||||
if (file_info.exists()) {
|
||||
BootGame(filename.toLocal8Bit().data());
|
||||
BootGame(filename.toStdString());
|
||||
} else {
|
||||
// Display an error message and remove the file from the list.
|
||||
QMessageBox::information(this, tr("File not found"), tr("File \"%1\" not found").arg(filename));
|
||||
|
||||
QSettings settings;
|
||||
QStringList recent_files = settings.value("recentFiles").toStringList();
|
||||
recent_files.removeOne(filename);
|
||||
settings.setValue("recentFiles", recent_files);
|
||||
UISettings::values.recent_files.removeOne(filename);
|
||||
UpdateRecentFiles();
|
||||
}
|
||||
}
|
||||
@ -496,31 +463,6 @@ void GMainWindow::OnStopGame() {
|
||||
ShutdownGame();
|
||||
}
|
||||
|
||||
void GMainWindow::OnOpenHotkeysDialog() {
|
||||
GHotkeysDialog dialog(this);
|
||||
dialog.exec();
|
||||
}
|
||||
|
||||
void GMainWindow::SetHardwareRendererEnabled(bool enabled) {
|
||||
VideoCore::g_hw_renderer_enabled = enabled;
|
||||
|
||||
Config config;
|
||||
Settings::values.use_hw_renderer = enabled;
|
||||
config.Save();
|
||||
}
|
||||
|
||||
void GMainWindow::SetGdbstubEnabled(bool enabled) {
|
||||
GDBStub::ToggleServer(enabled);
|
||||
}
|
||||
|
||||
void GMainWindow::SetShaderJITEnabled(bool enabled) {
|
||||
VideoCore::g_shader_jit_enabled = enabled;
|
||||
|
||||
Config config;
|
||||
Settings::values.use_shader_jit = enabled;
|
||||
config.Save();
|
||||
}
|
||||
|
||||
void GMainWindow::ToggleWindowMode() {
|
||||
if (ui.action_Single_Window_Mode->isChecked()) {
|
||||
// Render in the main window...
|
||||
@ -547,11 +489,17 @@ void GMainWindow::ToggleWindowMode() {
|
||||
}
|
||||
|
||||
void GMainWindow::OnConfigure() {
|
||||
//GControllerConfigDialog* dialog = new GControllerConfigDialog(controller_ports, this);
|
||||
ConfigureDialog configureDialog(this);
|
||||
auto result = configureDialog.exec();
|
||||
if (result == QDialog::Accepted)
|
||||
{
|
||||
configureDialog.applyConfiguration();
|
||||
config->Save();
|
||||
}
|
||||
}
|
||||
|
||||
bool GMainWindow::ConfirmClose() {
|
||||
if (emu_thread == nullptr || !confirm_before_closing)
|
||||
if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
|
||||
return true;
|
||||
|
||||
auto answer = QMessageBox::question(this, tr("Citra"),
|
||||
@ -566,23 +514,19 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save window layout
|
||||
QSettings settings(QSettings::IniFormat, QSettings::UserScope, "Citra team", "Citra");
|
||||
UISettings::values.geometry = saveGeometry();
|
||||
UISettings::values.state = saveState();
|
||||
UISettings::values.renderwindow_geometry = render_window->saveGeometry();
|
||||
#if MICROPROFILE_ENABLED
|
||||
UISettings::values.microprofile_geometry = microProfileDialog->saveGeometry();
|
||||
UISettings::values.microprofile_visible = microProfileDialog->isVisible();
|
||||
#endif
|
||||
UISettings::values.single_window_mode = ui.action_Single_Window_Mode->isChecked();
|
||||
UISettings::values.display_titlebar = ui.actionDisplay_widget_title_bars->isChecked();
|
||||
UISettings::values.first_start = false;
|
||||
|
||||
settings.beginGroup("UILayout");
|
||||
settings.setValue("geometry", saveGeometry());
|
||||
settings.setValue("state", saveState());
|
||||
settings.setValue("geometryRenderWindow", render_window->saveGeometry());
|
||||
settings.setValue("microProfileDialogGeometry", microProfileDialog->saveGeometry());
|
||||
settings.setValue("microProfileDialogVisible", microProfileDialog->isVisible());
|
||||
settings.endGroup();
|
||||
|
||||
settings.setValue("singleWindowMode", ui.action_Single_Window_Mode->isChecked());
|
||||
settings.setValue("displayTitleBars", ui.actionDisplay_widget_title_bars->isChecked());
|
||||
settings.setValue("firstStart", false);
|
||||
settings.setValue("confirmClose", confirm_before_closing);
|
||||
game_list->SaveInterfaceLayout(settings);
|
||||
SaveHotkeys(settings);
|
||||
game_list->SaveInterfaceLayout();
|
||||
SaveHotkeys();
|
||||
|
||||
// Shutdown session if the emu thread is active...
|
||||
if (emu_thread != nullptr)
|
||||
@ -607,7 +551,6 @@ int main(int argc, char* argv[]) {
|
||||
});
|
||||
|
||||
// Init settings params
|
||||
QSettings::setDefaultFormat(QSettings::IniFormat);
|
||||
QCoreApplication::setOrganizationName("Citra team");
|
||||
QCoreApplication::setApplicationName("Citra");
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "ui_main.h"
|
||||
|
||||
class Config;
|
||||
class GameList;
|
||||
class GImageInfo;
|
||||
class GRenderWindow;
|
||||
@ -104,12 +105,8 @@ private slots:
|
||||
/// Called whenever a user selects the "File->Select Game List Root" menu item
|
||||
void OnMenuSelectGameListRoot();
|
||||
void OnMenuRecentFile();
|
||||
void OnOpenHotkeysDialog();
|
||||
void OnConfigure();
|
||||
void OnDisplayTitleBars(bool);
|
||||
void SetHardwareRendererEnabled(bool);
|
||||
void SetGdbstubEnabled(bool);
|
||||
void SetShaderJITEnabled(bool);
|
||||
void ToggleWindowMode();
|
||||
|
||||
private:
|
||||
@ -118,6 +115,8 @@ private:
|
||||
GRenderWindow* render_window;
|
||||
GameList* game_list;
|
||||
|
||||
std::unique_ptr<Config> config;
|
||||
|
||||
// Whether emulation is currently running in Citra.
|
||||
bool emulation_running = false;
|
||||
std::unique_ptr<EmuThread> emu_thread;
|
||||
@ -131,7 +130,6 @@ private:
|
||||
GPUCommandListWidget* graphicsCommandsWidget;
|
||||
|
||||
QAction* actions_recent_files[max_recent_files_item];
|
||||
bool confirm_before_closing;
|
||||
};
|
||||
|
||||
#endif // _CITRA_QT_MAIN_HXX_
|
||||
|
@ -45,7 +45,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1081</width>
|
||||
<height>22</height>
|
||||
<height>19</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menu_File">
|
||||
@ -73,9 +73,6 @@
|
||||
<addaction name="action_Pause"/>
|
||||
<addaction name="action_Stop"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Use_Hardware_Renderer"/>
|
||||
<addaction name="action_Use_Shader_JIT"/>
|
||||
<addaction name="action_Use_Gdbstub"/>
|
||||
<addaction name="action_Configure"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_View">
|
||||
@ -84,7 +81,6 @@
|
||||
</property>
|
||||
<addaction name="action_Single_Window_Mode"/>
|
||||
<addaction name="actionDisplay_widget_title_bars"/>
|
||||
<addaction name="action_Hotkeys"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_Help">
|
||||
<property name="title">
|
||||
@ -150,35 +146,6 @@
|
||||
<string>Single Window Mode</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Hotkeys">
|
||||
<property name="text">
|
||||
<string>Configure &Hotkeys ...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Use_Hardware_Renderer">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use Hardware Renderer</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Use_Shader_JIT">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use Shader JIT</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Use_Gdbstub">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use Gdbstub</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Configure">
|
||||
<property name="text">
|
||||
<string>Configure ...</string>
|
||||
@ -219,22 +186,6 @@
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>action_Configure</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>OnConfigure()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>540</x>
|
||||
<y>364</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionDisplay_widget_title_bars</sender>
|
||||
<signal>triggered(bool)</signal>
|
||||
|
11
src/citra_qt/ui_settings.cpp
Normal file
11
src/citra_qt/ui_settings.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "ui_settings.h"
|
||||
|
||||
namespace UISettings {
|
||||
|
||||
Values values = {};
|
||||
|
||||
}
|
47
src/citra_qt/ui_settings.h
Normal file
47
src/citra_qt/ui_settings.h
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QStringList>
|
||||
#include <QString>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace UISettings {
|
||||
|
||||
using ContextualShortcut = std::pair<QString, int> ;
|
||||
using Shortcut = std::pair<QString, ContextualShortcut>;
|
||||
|
||||
struct Values {
|
||||
QByteArray geometry;
|
||||
QByteArray state;
|
||||
|
||||
QByteArray renderwindow_geometry;
|
||||
|
||||
QByteArray gamelist_header_state;
|
||||
|
||||
QByteArray microprofile_geometry;
|
||||
bool microprofile_visible;
|
||||
|
||||
bool single_window_mode;
|
||||
bool display_titlebar;
|
||||
|
||||
bool confirm_before_closing;
|
||||
bool first_start;
|
||||
|
||||
QString roms_path;
|
||||
QString symbols_path;
|
||||
QString gamedir;
|
||||
bool gamedir_deepscan;
|
||||
QStringList recent_files;
|
||||
|
||||
// Shortcut name <Shortcut, context>
|
||||
std::vector<Shortcut> shortcuts;
|
||||
};
|
||||
|
||||
extern Values values;
|
||||
|
||||
}
|
@ -42,13 +42,11 @@ set(HEADERS
|
||||
logging/filter.h
|
||||
logging/log.h
|
||||
logging/backend.h
|
||||
make_unique.h
|
||||
math_util.h
|
||||
memory_util.h
|
||||
microprofile.h
|
||||
microprofileui.h
|
||||
platform.h
|
||||
profiler.h
|
||||
profiler_reporting.h
|
||||
scm_rev.h
|
||||
scope_exit.h
|
||||
|
@ -39,6 +39,7 @@ static void assert_noinline_call(const Fn& fn) {
|
||||
}); } while (0)
|
||||
|
||||
#define UNREACHABLE() ASSERT_MSG(false, "Unreachable code!")
|
||||
#define UNREACHABLE_MSG(...) ASSERT_MSG(false, __VA_ARGS__)
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define DEBUG_ASSERT(_a_) ASSERT(_a_)
|
||||
@ -49,3 +50,4 @@ static void assert_noinline_call(const Fn& fn) {
|
||||
#endif
|
||||
|
||||
#define UNIMPLEMENTED() DEBUG_ASSERT_MSG(false, "Unimplemented code!")
|
||||
#define UNIMPLEMENTED_MSG(_a_, ...) ASSERT_MSG(false, _a_, __VA_ARGS__)
|
@ -69,9 +69,10 @@ static void StripTailDirSlashes(std::string &fname)
|
||||
{
|
||||
if (fname.length() > 1)
|
||||
{
|
||||
size_t i = fname.length() - 1;
|
||||
while (fname[i] == DIR_SEP_CHR)
|
||||
fname[i--] = '\0';
|
||||
size_t i = fname.length();
|
||||
while (i > 0 && fname[i - 1] == DIR_SEP_CHR)
|
||||
--i;
|
||||
fname.resize(i);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -85,7 +86,11 @@ bool Exists(const std::string &filename)
|
||||
StripTailDirSlashes(copy);
|
||||
|
||||
#ifdef _WIN32
|
||||
int result = _tstat64(Common::UTF8ToTStr(copy).c_str(), &file_info);
|
||||
// Windows needs a slash to identify a driver root
|
||||
if (copy.size() != 0 && copy.back() == ':')
|
||||
copy += DIR_SEP_CHR;
|
||||
|
||||
int result = _wstat64(Common::UTF8ToUTF16W(copy).c_str(), &file_info);
|
||||
#else
|
||||
int result = stat64(copy.c_str(), &file_info);
|
||||
#endif
|
||||
@ -102,7 +107,11 @@ bool IsDirectory(const std::string &filename)
|
||||
StripTailDirSlashes(copy);
|
||||
|
||||
#ifdef _WIN32
|
||||
int result = _tstat64(Common::UTF8ToTStr(copy).c_str(), &file_info);
|
||||
// Windows needs a slash to identify a driver root
|
||||
if (copy.size() != 0 && copy.back() == ':')
|
||||
copy += DIR_SEP_CHR;
|
||||
|
||||
int result = _wstat64(Common::UTF8ToUTF16W(copy).c_str(), &file_info);
|
||||
#else
|
||||
int result = stat64(copy.c_str(), &file_info);
|
||||
#endif
|
||||
@ -138,7 +147,7 @@ bool Delete(const std::string &filename)
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (!DeleteFile(Common::UTF8ToTStr(filename).c_str()))
|
||||
if (!DeleteFileW(Common::UTF8ToUTF16W(filename).c_str()))
|
||||
{
|
||||
LOG_ERROR(Common_Filesystem, "DeleteFile failed on %s: %s",
|
||||
filename.c_str(), GetLastErrorMsg());
|
||||
@ -160,7 +169,7 @@ bool CreateDir(const std::string &path)
|
||||
{
|
||||
LOG_TRACE(Common_Filesystem, "directory %s", path.c_str());
|
||||
#ifdef _WIN32
|
||||
if (::CreateDirectory(Common::UTF8ToTStr(path).c_str(), nullptr))
|
||||
if (::CreateDirectoryW(Common::UTF8ToUTF16W(path).c_str(), nullptr))
|
||||
return true;
|
||||
DWORD error = GetLastError();
|
||||
if (error == ERROR_ALREADY_EXISTS)
|
||||
@ -241,7 +250,7 @@ bool DeleteDir(const std::string &filename)
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (::RemoveDirectory(Common::UTF8ToTStr(filename).c_str()))
|
||||
if (::RemoveDirectoryW(Common::UTF8ToUTF16W(filename).c_str()))
|
||||
return true;
|
||||
#else
|
||||
if (rmdir(filename.c_str()) == 0)
|
||||
@ -257,8 +266,13 @@ bool Rename(const std::string &srcFilename, const std::string &destFilename)
|
||||
{
|
||||
LOG_TRACE(Common_Filesystem, "%s --> %s",
|
||||
srcFilename.c_str(), destFilename.c_str());
|
||||
#ifdef _WIN32
|
||||
if (_wrename(Common::UTF8ToUTF16W(srcFilename).c_str(), Common::UTF8ToUTF16W(destFilename).c_str()) == 0)
|
||||
return true;
|
||||
#else
|
||||
if (rename(srcFilename.c_str(), destFilename.c_str()) == 0)
|
||||
return true;
|
||||
#endif
|
||||
LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s",
|
||||
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
|
||||
return false;
|
||||
@ -270,7 +284,7 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename)
|
||||
LOG_TRACE(Common_Filesystem, "%s --> %s",
|
||||
srcFilename.c_str(), destFilename.c_str());
|
||||
#ifdef _WIN32
|
||||
if (CopyFile(Common::UTF8ToTStr(srcFilename).c_str(), Common::UTF8ToTStr(destFilename).c_str(), FALSE))
|
||||
if (CopyFileW(Common::UTF8ToUTF16W(srcFilename).c_str(), Common::UTF8ToUTF16W(destFilename).c_str(), FALSE))
|
||||
return true;
|
||||
|
||||
LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s",
|
||||
@ -358,7 +372,7 @@ u64 GetSize(const std::string &filename)
|
||||
|
||||
struct stat64 buf;
|
||||
#ifdef _WIN32
|
||||
if (_tstat64(Common::UTF8ToTStr(filename).c_str(), &buf) == 0)
|
||||
if (_wstat64(Common::UTF8ToUTF16W(filename).c_str(), &buf) == 0)
|
||||
#else
|
||||
if (stat64(filename.c_str(), &buf) == 0)
|
||||
#endif
|
||||
@ -432,16 +446,16 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo
|
||||
|
||||
#ifdef _WIN32
|
||||
// Find the first file in the directory.
|
||||
WIN32_FIND_DATA ffd;
|
||||
WIN32_FIND_DATAW ffd;
|
||||
|
||||
HANDLE handle_find = FindFirstFile(Common::UTF8ToTStr(directory + "\\*").c_str(), &ffd);
|
||||
HANDLE handle_find = FindFirstFileW(Common::UTF8ToUTF16W(directory + "\\*").c_str(), &ffd);
|
||||
if (handle_find == INVALID_HANDLE_VALUE) {
|
||||
FindClose(handle_find);
|
||||
return false;
|
||||
}
|
||||
// windows loop
|
||||
do {
|
||||
const std::string virtual_name(Common::TStrToUTF8(ffd.cFileName));
|
||||
const std::string virtual_name(Common::UTF16ToUTF8(ffd.cFileName));
|
||||
#else
|
||||
struct dirent dirent, *result = nullptr;
|
||||
|
||||
@ -465,7 +479,7 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo
|
||||
found_entries += ret_entries;
|
||||
|
||||
#ifdef _WIN32
|
||||
} while (FindNextFile(handle_find, &ffd) != 0);
|
||||
} while (FindNextFileW(handle_find, &ffd) != 0);
|
||||
FindClose(handle_find);
|
||||
#else
|
||||
}
|
||||
@ -572,15 +586,23 @@ void CopyDir(const std::string &source_path, const std::string &dest_path)
|
||||
// Returns the current directory
|
||||
std::string GetCurrentDir()
|
||||
{
|
||||
char *dir;
|
||||
// Get the current working directory (getcwd uses malloc)
|
||||
#ifdef _WIN32
|
||||
wchar_t *dir;
|
||||
if (!(dir = _wgetcwd(nullptr, 0))) {
|
||||
#else
|
||||
char *dir;
|
||||
if (!(dir = getcwd(nullptr, 0))) {
|
||||
|
||||
#endif
|
||||
LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: %s",
|
||||
GetLastErrorMsg());
|
||||
return nullptr;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
std::string strDir = Common::UTF16ToUTF8(dir);
|
||||
#else
|
||||
std::string strDir = dir;
|
||||
#endif
|
||||
free(dir);
|
||||
return strDir;
|
||||
}
|
||||
@ -588,7 +610,11 @@ std::string GetCurrentDir()
|
||||
// Sets the current directory to the given directory
|
||||
bool SetCurrentDir(const std::string &directory)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return _wchdir(Common::UTF8ToUTF16W(directory).c_str()) == 0;
|
||||
#else
|
||||
return chdir(directory.c_str()) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
@ -613,9 +639,9 @@ std::string& GetExeDirectory()
|
||||
static std::string exe_path;
|
||||
if (exe_path.empty())
|
||||
{
|
||||
TCHAR tchar_exe_path[2048];
|
||||
GetModuleFileName(nullptr, tchar_exe_path, 2048);
|
||||
exe_path = Common::TStrToUTF8(tchar_exe_path);
|
||||
wchar_t wchar_exe_path[2048];
|
||||
GetModuleFileNameW(nullptr, wchar_exe_path, 2048);
|
||||
exe_path = Common::UTF16ToUTF8(wchar_exe_path);
|
||||
exe_path = exe_path.substr(0, exe_path.find_last_of('\\'));
|
||||
}
|
||||
return exe_path;
|
||||
@ -807,13 +833,12 @@ size_t WriteStringToFile(bool text_file, const std::string &str, const char *fil
|
||||
|
||||
size_t ReadFileToString(bool text_file, const char *filename, std::string &str)
|
||||
{
|
||||
FileUtil::IOFile file(filename, text_file ? "r" : "rb");
|
||||
auto const f = file.GetHandle();
|
||||
IOFile file(filename, text_file ? "r" : "rb");
|
||||
|
||||
if (!f)
|
||||
if (!file)
|
||||
return false;
|
||||
|
||||
str.resize(static_cast<u32>(GetSize(f)));
|
||||
str.resize(static_cast<u32>(file.GetSize()));
|
||||
return file.ReadArray(&str[0], str.size());
|
||||
}
|
||||
|
||||
@ -860,15 +885,10 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
|
||||
}
|
||||
|
||||
IOFile::IOFile()
|
||||
: m_file(nullptr), m_good(true)
|
||||
{}
|
||||
|
||||
IOFile::IOFile(std::FILE* file)
|
||||
: m_file(file), m_good(true)
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
IOFile::IOFile(const std::string& filename, const char openmode[])
|
||||
: m_file(nullptr), m_good(true)
|
||||
{
|
||||
Open(filename, openmode);
|
||||
}
|
||||
@ -879,7 +899,6 @@ IOFile::~IOFile()
|
||||
}
|
||||
|
||||
IOFile::IOFile(IOFile&& other)
|
||||
: m_file(nullptr), m_good(true)
|
||||
{
|
||||
Swap(other);
|
||||
}
|
||||
@ -900,7 +919,7 @@ bool IOFile::Open(const std::string& filename, const char openmode[])
|
||||
{
|
||||
Close();
|
||||
#ifdef _WIN32
|
||||
_tfopen_s(&m_file, Common::UTF8ToTStr(filename).c_str(), Common::UTF8ToTStr(openmode).c_str());
|
||||
_wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(), Common::UTF8ToUTF16W(openmode).c_str());
|
||||
#else
|
||||
m_file = fopen(filename.c_str(), openmode);
|
||||
#endif
|
||||
@ -918,26 +937,12 @@ bool IOFile::Close()
|
||||
return m_good;
|
||||
}
|
||||
|
||||
std::FILE* IOFile::ReleaseHandle()
|
||||
{
|
||||
std::FILE* const ret = m_file;
|
||||
m_file = nullptr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void IOFile::SetHandle(std::FILE* file)
|
||||
{
|
||||
Close();
|
||||
Clear();
|
||||
m_file = file;
|
||||
}
|
||||
|
||||
u64 IOFile::GetSize()
|
||||
u64 IOFile::GetSize() const
|
||||
{
|
||||
if (IsOpen())
|
||||
return FileUtil::GetSize(m_file);
|
||||
else
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool IOFile::Seek(s64 off, int origin)
|
||||
@ -948,12 +953,12 @@ bool IOFile::Seek(s64 off, int origin)
|
||||
return m_good;
|
||||
}
|
||||
|
||||
u64 IOFile::Tell()
|
||||
u64 IOFile::Tell() const
|
||||
{
|
||||
if (IsOpen())
|
||||
return ftello(m_file);
|
||||
else
|
||||
return -1;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool IOFile::Flush()
|
||||
|
@ -14,6 +14,10 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include "common/string_util.h"
|
||||
#endif
|
||||
|
||||
// User directory indices for GetUserPath
|
||||
enum {
|
||||
D_USER_IDX,
|
||||
@ -172,7 +176,6 @@ class IOFile : public NonCopyable
|
||||
{
|
||||
public:
|
||||
IOFile();
|
||||
IOFile(std::FILE* file);
|
||||
IOFile(const std::string& filename, const char openmode[]);
|
||||
|
||||
~IOFile();
|
||||
@ -188,6 +191,11 @@ public:
|
||||
template <typename T>
|
||||
size_t ReadArray(T* data, size_t length)
|
||||
{
|
||||
static_assert(std::is_standard_layout<T>(), "Given array does not consist of standard layout objects");
|
||||
#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER)
|
||||
static_assert(std::is_trivially_copyable<T>(), "Given array does not consist of trivially copyable objects");
|
||||
#endif
|
||||
|
||||
if (!IsOpen()) {
|
||||
m_good = false;
|
||||
return -1;
|
||||
@ -203,9 +211,10 @@ public:
|
||||
template <typename T>
|
||||
size_t WriteArray(const T* data, size_t length)
|
||||
{
|
||||
static_assert(std::is_standard_layout<T>::value, "Given array does not consist of standard layout objects");
|
||||
// TODO: gcc 4.8 does not support is_trivially_copyable, but we really should check for it here.
|
||||
//static_assert(std::is_trivially_copyable<T>::value, "Given array does not consist of trivially copyable objects");
|
||||
static_assert(std::is_standard_layout<T>(), "Given array does not consist of standard layout objects");
|
||||
#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER)
|
||||
static_assert(std::is_trivially_copyable<T>(), "Given array does not consist of trivially copyable objects");
|
||||
#endif
|
||||
|
||||
if (!IsOpen()) {
|
||||
m_good = false;
|
||||
@ -235,32 +244,24 @@ public:
|
||||
return WriteArray(&object, 1);
|
||||
}
|
||||
|
||||
bool IsOpen() { return nullptr != m_file; }
|
||||
bool IsOpen() const { return nullptr != m_file; }
|
||||
|
||||
// m_good is set to false when a read, write or other function fails
|
||||
bool IsGood() { return m_good; }
|
||||
operator void*() { return m_good ? m_file : nullptr; }
|
||||
|
||||
std::FILE* ReleaseHandle();
|
||||
|
||||
std::FILE* GetHandle() { return m_file; }
|
||||
|
||||
void SetHandle(std::FILE* file);
|
||||
bool IsGood() const { return m_good; }
|
||||
explicit operator bool() const { return IsGood(); }
|
||||
|
||||
bool Seek(s64 off, int origin);
|
||||
u64 Tell();
|
||||
u64 GetSize();
|
||||
u64 Tell() const;
|
||||
u64 GetSize() const;
|
||||
bool Resize(u64 size);
|
||||
bool Flush();
|
||||
|
||||
// clear error state
|
||||
void Clear() { m_good = true; std::clearerr(m_file); }
|
||||
|
||||
std::FILE* m_file;
|
||||
bool m_good;
|
||||
private:
|
||||
IOFile(IOFile&);
|
||||
IOFile& operator=(IOFile& other);
|
||||
std::FILE* m_file = nullptr;
|
||||
bool m_good = true;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
@ -34,6 +34,7 @@ namespace Log {
|
||||
SUB(Kernel, SVC) \
|
||||
CLS(Service) \
|
||||
SUB(Service, SRV) \
|
||||
SUB(Service, FRD) \
|
||||
SUB(Service, FS) \
|
||||
SUB(Service, ERR) \
|
||||
SUB(Service, APT) \
|
||||
|
@ -49,6 +49,7 @@ enum class Class : ClassType {
|
||||
Service, ///< HLE implementation of system services. Each major service
|
||||
/// should have its own subclass.
|
||||
Service_SRV, ///< The SRV (Service Directory) implementation
|
||||
Service_FRD, ///< The FRD (Friends) service
|
||||
Service_FS, ///< The FS (Filesystem) service implementation
|
||||
Service_ERR, ///< The ERR (Error) port implementation
|
||||
Service_APT, ///< The APT (Applets) service
|
||||
@ -61,7 +62,7 @@ enum class Class : ClassType {
|
||||
Service_NIM, ///< The NIM (Network interface manager) service
|
||||
Service_NWM, ///< The NWM (Network wlan manager) service
|
||||
Service_CAM, ///< The CAM (Camera) service
|
||||
Service_CECD, ///< The CECD service
|
||||
Service_CECD, ///< The CECD (StreetPass) service
|
||||
Service_CFG, ///< The CFG (Configuration) service
|
||||
Service_DSP, ///< The DSP (DSP control) service
|
||||
Service_DLP, ///< The DLP (Download Play) service
|
||||
|
@ -1,17 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <typename T, typename... Args>
|
||||
std::unique_ptr<T> make_unique(Args&&... args) {
|
||||
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
} // namespace
|
@ -4,6 +4,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
// Uncomment this to disable microprofile. This will get you cleaner profiles when using
|
||||
// external sampling profilers like "Very Sleepy", and will improve performance somewhat.
|
||||
// #define MICROPROFILE_ENABLED 0
|
||||
|
||||
// Customized Citra settings.
|
||||
// This file wraps the MicroProfile header so that these are consistent everywhere.
|
||||
#define MICROPROFILE_WEBSERVER 0
|
||||
|
@ -13,4 +13,7 @@
|
||||
#define MICROPROFILE_HELP_ALT "Right-Click"
|
||||
#define MICROPROFILE_HELP_MOD "Ctrl"
|
||||
|
||||
// This isn't included by microprofileui.h :(
|
||||
#include <cstdlib> // For std::abs
|
||||
|
||||
#include <microprofileui.h>
|
||||
|
@ -7,71 +7,16 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/profiler.h"
|
||||
#include "common/profiler_reporting.h"
|
||||
#include "common/synchronized_wrapper.h"
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013.
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h> // For QueryPerformanceCounter/Frequency
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
namespace Profiling {
|
||||
|
||||
#if ENABLE_PROFILING
|
||||
thread_local Timer* Timer::current_timer = nullptr;
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013
|
||||
QPCClock::time_point QPCClock::now() {
|
||||
static LARGE_INTEGER freq;
|
||||
// Use this dummy local static to ensure this gets initialized once.
|
||||
static BOOL dummy = QueryPerformanceFrequency(&freq);
|
||||
|
||||
LARGE_INTEGER ticks;
|
||||
QueryPerformanceCounter(&ticks);
|
||||
|
||||
// This is prone to overflow when multiplying, which is why I'm using micro instead of nano. The
|
||||
// correct way to approach this would be to just return ticks as a time_point and then subtract
|
||||
// and do this conversion when creating a duration from two time_points, however, as far as I
|
||||
// could tell the C++ requirements for these types are incompatible with this approach.
|
||||
return time_point(duration(ticks.QuadPart * std::micro::den / freq.QuadPart));
|
||||
}
|
||||
#endif
|
||||
|
||||
TimingCategory::TimingCategory(const char* name, TimingCategory* parent)
|
||||
: accumulated_duration(0) {
|
||||
|
||||
ProfilingManager& manager = GetProfilingManager();
|
||||
category_id = manager.RegisterTimingCategory(this, name);
|
||||
if (parent != nullptr)
|
||||
manager.SetTimingCategoryParent(category_id, parent->category_id);
|
||||
}
|
||||
|
||||
ProfilingManager::ProfilingManager()
|
||||
: last_frame_end(Clock::now()), this_frame_start(Clock::now()) {
|
||||
}
|
||||
|
||||
unsigned int ProfilingManager::RegisterTimingCategory(TimingCategory* category, const char* name) {
|
||||
TimingCategoryInfo info;
|
||||
info.category = category;
|
||||
info.name = name;
|
||||
info.parent = TimingCategoryInfo::NO_PARENT;
|
||||
|
||||
unsigned int id = (unsigned int)timing_categories.size();
|
||||
timing_categories.push_back(std::move(info));
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void ProfilingManager::SetTimingCategoryParent(unsigned int category, unsigned int parent) {
|
||||
ASSERT(category < timing_categories.size());
|
||||
ASSERT(parent < timing_categories.size());
|
||||
|
||||
timing_categories[category].parent = parent;
|
||||
}
|
||||
|
||||
void ProfilingManager::BeginFrame() {
|
||||
this_frame_start = Clock::now();
|
||||
}
|
||||
@ -82,11 +27,6 @@ void ProfilingManager::FinishFrame() {
|
||||
results.interframe_time = now - last_frame_end;
|
||||
results.frame_time = now - this_frame_start;
|
||||
|
||||
results.time_per_category.resize(timing_categories.size());
|
||||
for (size_t i = 0; i < timing_categories.size(); ++i) {
|
||||
results.time_per_category[i] = timing_categories[i].category->GetAccumulatedTime();
|
||||
}
|
||||
|
||||
last_frame_end = now;
|
||||
}
|
||||
|
||||
@ -100,26 +40,9 @@ void TimingResultsAggregator::Clear() {
|
||||
window_size = cursor = 0;
|
||||
}
|
||||
|
||||
void TimingResultsAggregator::SetNumberOfCategories(size_t n) {
|
||||
size_t old_size = times_per_category.size();
|
||||
if (n == old_size)
|
||||
return;
|
||||
|
||||
times_per_category.resize(n);
|
||||
|
||||
for (size_t i = old_size; i < n; ++i) {
|
||||
times_per_category[i].resize(max_window_size, Duration::zero());
|
||||
}
|
||||
}
|
||||
|
||||
void TimingResultsAggregator::AddFrame(const ProfilingFrameResult& frame_result) {
|
||||
SetNumberOfCategories(frame_result.time_per_category.size());
|
||||
|
||||
interframe_times[cursor] = frame_result.interframe_time;
|
||||
frame_times[cursor] = frame_result.frame_time;
|
||||
for (size_t i = 0; i < frame_result.time_per_category.size(); ++i) {
|
||||
times_per_category[i][cursor] = frame_result.time_per_category[i];
|
||||
}
|
||||
|
||||
++cursor;
|
||||
if (cursor == max_window_size)
|
||||
@ -162,11 +85,6 @@ AggregatedFrameResult TimingResultsAggregator::GetAggregatedResults() const {
|
||||
result.fps = 0.0f;
|
||||
}
|
||||
|
||||
result.time_per_category.resize(times_per_category.size());
|
||||
for (size_t i = 0; i < times_per_category.size(); ++i) {
|
||||
result.time_per_category[i] = AggregateField(times_per_category[i], window_size);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -1,152 +0,0 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/thread.h"
|
||||
|
||||
namespace Common {
|
||||
namespace Profiling {
|
||||
|
||||
// If this is defined to 0, it turns all Timers into no-ops.
|
||||
#ifndef ENABLE_PROFILING
|
||||
#define ENABLE_PROFILING 1
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013
|
||||
// MSVC up to 2013 doesn't use QueryPerformanceCounter for high_resolution_clock, so it has bad
|
||||
// precision. We manually implement a clock based on QPC to get good results.
|
||||
|
||||
struct QPCClock {
|
||||
using duration = std::chrono::microseconds;
|
||||
using time_point = std::chrono::time_point<QPCClock>;
|
||||
using rep = duration::rep;
|
||||
using period = duration::period;
|
||||
static const bool is_steady = false;
|
||||
|
||||
static time_point now();
|
||||
};
|
||||
|
||||
using Clock = QPCClock;
|
||||
#else
|
||||
using Clock = std::chrono::high_resolution_clock;
|
||||
#endif
|
||||
|
||||
using Duration = Clock::duration;
|
||||
|
||||
/**
|
||||
* Represents a timing category that measured time can be accounted towards. Should be declared as a
|
||||
* global variable and passed to Timers.
|
||||
*/
|
||||
class TimingCategory final {
|
||||
public:
|
||||
TimingCategory(const char* name, TimingCategory* parent = nullptr);
|
||||
|
||||
unsigned int GetCategoryId() const {
|
||||
return category_id;
|
||||
}
|
||||
|
||||
/// Adds some time to this category. Can safely be called from multiple threads at the same time.
|
||||
void AddTime(Duration amount) {
|
||||
std::atomic_fetch_add_explicit(
|
||||
&accumulated_duration, amount.count(),
|
||||
std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically retrieves the accumulated measured time for this category and resets the counter
|
||||
* to zero. Can be safely called concurrently with AddTime.
|
||||
*/
|
||||
Duration GetAccumulatedTime() {
|
||||
return Duration(std::atomic_exchange_explicit(
|
||||
&accumulated_duration, (Duration::rep)0,
|
||||
std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int category_id;
|
||||
std::atomic<Duration::rep> accumulated_duration;
|
||||
};
|
||||
|
||||
/**
|
||||
* Measures time elapsed between a call to Start and a call to Stop and attributes it to the given
|
||||
* TimingCategory. Start/Stop can be called multiple times on the same timer, but each call must be
|
||||
* appropriately paired.
|
||||
*
|
||||
* When a Timer is started, it automatically pauses a previously running timer on the same thread,
|
||||
* which is resumed when it is stopped. As such, no special action needs to be taken to avoid
|
||||
* double-accounting of time on two categories.
|
||||
*/
|
||||
class Timer {
|
||||
public:
|
||||
Timer(TimingCategory& category) : category(category) {
|
||||
}
|
||||
|
||||
void Start() {
|
||||
#if ENABLE_PROFILING
|
||||
ASSERT(!running);
|
||||
previous_timer = current_timer;
|
||||
current_timer = this;
|
||||
if (previous_timer != nullptr)
|
||||
previous_timer->StopTiming();
|
||||
|
||||
StartTiming();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
#if ENABLE_PROFILING
|
||||
ASSERT(running);
|
||||
StopTiming();
|
||||
|
||||
if (previous_timer != nullptr)
|
||||
previous_timer->StartTiming();
|
||||
current_timer = previous_timer;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
#if ENABLE_PROFILING
|
||||
void StartTiming() {
|
||||
start = Clock::now();
|
||||
running = true;
|
||||
}
|
||||
|
||||
void StopTiming() {
|
||||
auto duration = Clock::now() - start;
|
||||
running = false;
|
||||
category.AddTime(std::chrono::duration_cast<Duration>(duration));
|
||||
}
|
||||
|
||||
Clock::time_point start;
|
||||
bool running = false;
|
||||
|
||||
Timer* previous_timer;
|
||||
static thread_local Timer* current_timer;
|
||||
#endif
|
||||
|
||||
TimingCategory& category;
|
||||
};
|
||||
|
||||
/**
|
||||
* A Timer that automatically starts timing when created and stops at the end of the scope. Should
|
||||
* be used in the majority of cases.
|
||||
*/
|
||||
class ScopeTimer : public Timer {
|
||||
public:
|
||||
ScopeTimer(TimingCategory& category) : Timer(category) {
|
||||
Start();
|
||||
}
|
||||
|
||||
~ScopeTimer() {
|
||||
Stop();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Profiling
|
||||
} // namespace Common
|
@ -4,22 +4,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
#include "common/profiler.h"
|
||||
#include "common/synchronized_wrapper.h"
|
||||
|
||||
namespace Common {
|
||||
namespace Profiling {
|
||||
|
||||
struct TimingCategoryInfo {
|
||||
static const unsigned int NO_PARENT = -1;
|
||||
|
||||
TimingCategory* category;
|
||||
const char* name;
|
||||
unsigned int parent;
|
||||
};
|
||||
using Clock = std::chrono::high_resolution_clock;
|
||||
using Duration = Clock::duration;
|
||||
|
||||
struct ProfilingFrameResult {
|
||||
/// Time since the last delivered frame
|
||||
@ -27,22 +22,12 @@ struct ProfilingFrameResult {
|
||||
|
||||
/// Time spent processing a frame, excluding VSync
|
||||
Duration frame_time;
|
||||
|
||||
/// Total amount of time spent inside each category in this frame. Indexed by the category id
|
||||
std::vector<Duration> time_per_category;
|
||||
};
|
||||
|
||||
class ProfilingManager final {
|
||||
public:
|
||||
ProfilingManager();
|
||||
|
||||
unsigned int RegisterTimingCategory(TimingCategory* category, const char* name);
|
||||
void SetTimingCategoryParent(unsigned int category, unsigned int parent);
|
||||
|
||||
const std::vector<TimingCategoryInfo>& GetTimingCategoriesInfo() const {
|
||||
return timing_categories;
|
||||
}
|
||||
|
||||
/// This should be called after swapping screen buffers.
|
||||
void BeginFrame();
|
||||
/// This should be called before swapping screen buffers.
|
||||
@ -54,7 +39,6 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<TimingCategoryInfo> timing_categories;
|
||||
Clock::time_point last_frame_end;
|
||||
Clock::time_point this_frame_start;
|
||||
|
||||
@ -73,9 +57,6 @@ struct AggregatedFrameResult {
|
||||
AggregatedDuration frame_time;
|
||||
|
||||
float fps;
|
||||
|
||||
/// Total amount of time spent inside each category in this frame. Indexed by the category id
|
||||
std::vector<AggregatedDuration> time_per_category;
|
||||
};
|
||||
|
||||
class TimingResultsAggregator final {
|
||||
@ -83,7 +64,6 @@ public:
|
||||
TimingResultsAggregator(size_t window_size);
|
||||
|
||||
void Clear();
|
||||
void SetNumberOfCategories(size_t n);
|
||||
|
||||
void AddFrame(const ProfilingFrameResult& frame_result);
|
||||
|
||||
@ -95,7 +75,6 @@ public:
|
||||
|
||||
std::vector<Duration> interframe_times;
|
||||
std::vector<Duration> frame_times;
|
||||
std::vector<std::vector<Duration>> times_per_category;
|
||||
};
|
||||
|
||||
ProfilingManager& GetProfilingManager();
|
||||
|
@ -320,19 +320,6 @@ std::u16string UTF8ToUTF16(const std::string& input)
|
||||
#endif
|
||||
}
|
||||
|
||||
static std::string UTF16ToUTF8(const std::wstring& input)
|
||||
{
|
||||
auto const size = WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), nullptr, 0, nullptr, nullptr);
|
||||
|
||||
std::string output;
|
||||
output.resize(size);
|
||||
|
||||
if (size == 0 || size != WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), &output[0], static_cast<int>(output.size()), nullptr, nullptr))
|
||||
output.clear();
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static std::wstring CPToUTF16(u32 code_page, const std::string& input)
|
||||
{
|
||||
auto const size = MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0);
|
||||
@ -346,6 +333,19 @@ static std::wstring CPToUTF16(u32 code_page, const std::string& input)
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string UTF16ToUTF8(const std::wstring& input)
|
||||
{
|
||||
auto const size = WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), nullptr, 0, nullptr, nullptr);
|
||||
|
||||
std::string output;
|
||||
output.resize(size);
|
||||
|
||||
if (size == 0 || size != WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), &output[0], static_cast<int>(output.size()), nullptr, nullptr))
|
||||
output.clear();
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::wstring UTF8ToUTF16W(const std::string &input)
|
||||
{
|
||||
return CPToUTF16(CP_UTF8, input);
|
||||
|
@ -95,7 +95,7 @@ std::string CP1252ToUTF8(const std::string& str);
|
||||
std::string SHIFTJISToUTF8(const std::string& str);
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
std::string UTF16ToUTF8(const std::wstring& input);
|
||||
std::wstring UTF8ToUTF16W(const std::string& str);
|
||||
|
||||
#ifdef _UNICODE
|
||||
|
@ -30,8 +30,7 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
namespace Common
|
||||
{
|
||||
namespace Common {
|
||||
|
||||
int CurrentThreadId();
|
||||
|
||||
@ -43,55 +42,55 @@ public:
|
||||
Event() : is_set(false) {}
|
||||
|
||||
void Set() {
|
||||
std::lock_guard<std::mutex> lk(m_mutex);
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
if (!is_set) {
|
||||
is_set = true;
|
||||
m_condvar.notify_one();
|
||||
condvar.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void Wait() {
|
||||
std::unique_lock<std::mutex> lk(m_mutex);
|
||||
m_condvar.wait(lk, [&]{ return is_set; });
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
condvar.wait(lk, [&]{ return is_set; });
|
||||
is_set = false;
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
std::unique_lock<std::mutex> lk(m_mutex);
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
// no other action required, since wait loops on the predicate and any lingering signal will get cleared on the first iteration
|
||||
is_set = false;
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_set;
|
||||
std::condition_variable m_condvar;
|
||||
std::mutex m_mutex;
|
||||
std::condition_variable condvar;
|
||||
std::mutex mutex;
|
||||
};
|
||||
|
||||
class Barrier {
|
||||
public:
|
||||
Barrier(size_t count) : m_count(count), m_waiting(0) {}
|
||||
explicit Barrier(size_t count_) : count(count_), waiting(0), generation(0) {}
|
||||
|
||||
/// Blocks until all "count" threads have called Sync()
|
||||
void Sync() {
|
||||
std::unique_lock<std::mutex> lk(m_mutex);
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
const size_t current_generation = generation;
|
||||
|
||||
// TODO: broken when next round of Sync()s
|
||||
// is entered before all waiting threads return from the notify_all
|
||||
|
||||
if (++m_waiting == m_count) {
|
||||
m_waiting = 0;
|
||||
m_condvar.notify_all();
|
||||
if (++waiting == count) {
|
||||
generation++;
|
||||
waiting = 0;
|
||||
condvar.notify_all();
|
||||
} else {
|
||||
m_condvar.wait(lk, [&]{ return m_waiting == 0; });
|
||||
condvar.wait(lk, [this, current_generation]{ return current_generation != generation; });
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::condition_variable m_condvar;
|
||||
std::mutex m_mutex;
|
||||
const size_t m_count;
|
||||
size_t m_waiting;
|
||||
std::condition_variable condvar;
|
||||
std::mutex mutex;
|
||||
const size_t count;
|
||||
size_t waiting;
|
||||
size_t generation; // Incremented once each time the barrier is used
|
||||
};
|
||||
|
||||
void SleepCurrentThread(int ms);
|
||||
@ -100,8 +99,7 @@ void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms
|
||||
// Use this function during a spin-wait to make the current thread
|
||||
// relax while another thread is working. This may be more efficient
|
||||
// than using events because event functions use kernel calls.
|
||||
inline void YieldCPU()
|
||||
{
|
||||
inline void YieldCPU() {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
|
@ -455,6 +455,18 @@ void XEmitter::CALL(const void* fnptr)
|
||||
Write32(u32(distance));
|
||||
}
|
||||
|
||||
FixupBranch XEmitter::CALL()
|
||||
{
|
||||
FixupBranch branch;
|
||||
branch.type = 1;
|
||||
branch.ptr = code + 5;
|
||||
|
||||
Write8(0xE8);
|
||||
Write32(0);
|
||||
|
||||
return branch;
|
||||
}
|
||||
|
||||
FixupBranch XEmitter::J(bool force5bytes)
|
||||
{
|
||||
FixupBranch branch;
|
||||
@ -531,6 +543,22 @@ void XEmitter::SetJumpTarget(const FixupBranch& branch)
|
||||
}
|
||||
}
|
||||
|
||||
void XEmitter::SetJumpTarget(const FixupBranch& branch, const u8* target)
|
||||
{
|
||||
if (branch.type == 0)
|
||||
{
|
||||
s64 distance = (s64)(target - branch.ptr);
|
||||
ASSERT_MSG(distance >= -0x80 && distance < 0x80, "Jump target too far away, needs force5Bytes = true");
|
||||
branch.ptr[-1] = (u8)(s8)distance;
|
||||
}
|
||||
else if (branch.type == 1)
|
||||
{
|
||||
s64 distance = (s64)(target - branch.ptr);
|
||||
ASSERT_MSG(distance >= -0x80000000LL && distance < 0x80000000LL, "Jump target too far away, needs indirect register");
|
||||
((s32*)branch.ptr)[-1] = (s32)distance;
|
||||
}
|
||||
}
|
||||
|
||||
//Single byte opcodes
|
||||
//There is no PUSHAD/POPAD in 64-bit mode.
|
||||
void XEmitter::INT3() {Write8(0xCC);}
|
||||
|
@ -425,12 +425,14 @@ public:
|
||||
#undef CALL
|
||||
#endif
|
||||
void CALL(const void* fnptr);
|
||||
FixupBranch CALL();
|
||||
void CALLptr(OpArg arg);
|
||||
|
||||
FixupBranch J_CC(CCFlags conditionCode, bool force5bytes = false);
|
||||
void J_CC(CCFlags conditionCode, const u8* addr, bool force5Bytes = false);
|
||||
|
||||
void SetJumpTarget(const FixupBranch& branch);
|
||||
void SetJumpTarget(const FixupBranch& branch, const u8* target);
|
||||
|
||||
void SETcc(CCFlags flag, OpArg dest);
|
||||
// Note: CMOV brings small if any benefit on current cpus.
|
||||
|
@ -3,8 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "common/make_unique.h"
|
||||
#include <memory>
|
||||
|
||||
#include "core/arm/skyeye_common/armstate.h"
|
||||
#include "core/arm/skyeye_common/armsupp.h"
|
||||
@ -18,7 +17,7 @@
|
||||
#include "core/core_timing.h"
|
||||
|
||||
ARM_DynCom::ARM_DynCom(PrivilegeMode initial_mode) {
|
||||
state = Common::make_unique<ARMul_State>(initial_mode);
|
||||
state = std::make_unique<ARMul_State>(initial_mode);
|
||||
}
|
||||
|
||||
ARM_DynCom::~ARM_DynCom() {
|
||||
@ -94,7 +93,7 @@ void ARM_DynCom::ResetContext(Core::ThreadContext& context, u32 stack_top, u32 e
|
||||
context.cpu_registers[0] = arg;
|
||||
context.pc = entry_point;
|
||||
context.sp = stack_top;
|
||||
context.cpsr = 0x1F; // Usermode
|
||||
context.cpsr = 0x1F | ((entry_point & 1) << 5); // Usermode and THUMB mode
|
||||
}
|
||||
|
||||
void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) {
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/profiler.h"
|
||||
|
||||
#include "core/memory.h"
|
||||
#include "core/hle/svc.h"
|
||||
@ -25,9 +24,6 @@
|
||||
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
|
||||
Common::Profiling::TimingCategory profile_execute("DynCom::Execute");
|
||||
Common::Profiling::TimingCategory profile_decode("DynCom::Decode");
|
||||
|
||||
enum {
|
||||
COND = (1 << 0),
|
||||
NON_BRANCH = (1 << 1),
|
||||
@ -3496,7 +3492,6 @@ static unsigned int InterpreterTranslateInstruction(const ARMul_State* cpu, cons
|
||||
}
|
||||
|
||||
static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr) {
|
||||
Common::Profiling::ScopeTimer timer_decode(profile_decode);
|
||||
MICROPROFILE_SCOPE(DynCom_Decode);
|
||||
|
||||
// Decode instruction, get index
|
||||
@ -3530,7 +3525,6 @@ static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr)
|
||||
}
|
||||
|
||||
static int InterpreterTranslateSingle(ARMul_State* cpu, int& bb_start, u32 addr) {
|
||||
Common::Profiling::ScopeTimer timer_decode(profile_decode);
|
||||
MICROPROFILE_SCOPE(DynCom_Decode);
|
||||
|
||||
ARM_INST_PTR inst_base = nullptr;
|
||||
@ -3565,7 +3559,6 @@ static int clz(unsigned int x) {
|
||||
MICROPROFILE_DEFINE(DynCom_Execute, "DynCom", "Execute", MP_RGB(255, 0, 0));
|
||||
|
||||
unsigned InterpreterMainLoop(ARMul_State* cpu) {
|
||||
Common::Profiling::ScopeTimer timer_execute(profile_execute);
|
||||
MICROPROFILE_SCOPE(DynCom_Execute);
|
||||
|
||||
GDBStub::BreakpointAddress breakpoint_data;
|
||||
@ -3955,9 +3948,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
|
||||
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
|
||||
add_inst* const inst_cream = (add_inst*)inst_base->component;
|
||||
|
||||
u32 rn_val = RN;
|
||||
if (inst_cream->Rn == 15)
|
||||
rn_val += 2 * cpu->GetInstructionSize();
|
||||
u32 rn_val = CHECK_READ_REG15_WA(cpu, inst_cream->Rn);
|
||||
|
||||
bool carry;
|
||||
bool overflow;
|
||||
@ -4082,11 +4073,12 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
|
||||
if ((inst_base->cond == ConditionCode::AL) || CondPassed(cpu, inst_base->cond)) {
|
||||
unsigned int inst = inst_cream->inst;
|
||||
if (BITS(inst, 20, 27) == 0x12 && BITS(inst, 4, 7) == 0x3) {
|
||||
const u32 jump_address = cpu->Reg[inst_cream->val.Rm];
|
||||
cpu->Reg[14] = (cpu->Reg[15] + cpu->GetInstructionSize());
|
||||
if(cpu->TFlag)
|
||||
cpu->Reg[14] |= 0x1;
|
||||
cpu->Reg[15] = cpu->Reg[inst_cream->val.Rm] & 0xfffffffe;
|
||||
cpu->TFlag = cpu->Reg[inst_cream->val.Rm] & 0x1;
|
||||
cpu->Reg[15] = jump_address & 0xfffffffe;
|
||||
cpu->TFlag = jump_address & 0x1;
|
||||
} else {
|
||||
cpu->Reg[14] = (cpu->Reg[15] + cpu->GetInstructionSize());
|
||||
cpu->TFlag = 0x1;
|
||||
@ -6167,9 +6159,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
|
||||
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
|
||||
sub_inst* const inst_cream = (sub_inst*)inst_base->component;
|
||||
|
||||
u32 rn_val = RN;
|
||||
if (inst_cream->Rn == 15)
|
||||
rn_val += 2 * cpu->GetInstructionSize();
|
||||
u32 rn_val = CHECK_READ_REG15_WA(cpu, inst_cream->Rn);
|
||||
|
||||
bool carry;
|
||||
bool overflow;
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/make_unique.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
#include "core/core.h"
|
||||
@ -74,8 +73,8 @@ void Stop() {
|
||||
|
||||
/// Initialize the core
|
||||
void Init() {
|
||||
g_sys_core = Common::make_unique<ARM_DynCom>(USER32MODE);
|
||||
g_app_core = Common::make_unique<ARM_DynCom>(USER32MODE);
|
||||
g_sys_core = std::make_unique<ARM_DynCom>(USER32MODE);
|
||||
g_app_core = std::make_unique<ARM_DynCom>(USER32MODE);
|
||||
|
||||
LOG_DEBUG(Core, "Initialized OK");
|
||||
}
|
||||
|
@ -87,6 +87,10 @@ void SetClockFrequencyMHz(int cpu_mhz) {
|
||||
FireMhzChange();
|
||||
}
|
||||
|
||||
int GetClockFrequency() {
|
||||
return g_clock_rate_arm11;
|
||||
}
|
||||
|
||||
int GetClockFrequencyMHz() {
|
||||
return g_clock_rate_arm11 / 1000000;
|
||||
}
|
||||
|
@ -134,6 +134,7 @@ void RegisterMHzChangeCallback(MHzChangeCallback callback);
|
||||
std::string GetScheduledEventsSummary();
|
||||
|
||||
void SetClockFrequencyMHz(int cpu_mhz);
|
||||
int GetClockFrequency();
|
||||
int GetClockFrequencyMHz();
|
||||
extern int g_slice_length;
|
||||
|
||||
|
@ -3,12 +3,12 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/make_unique.h"
|
||||
#include "common/string_util.h"
|
||||
|
||||
#include "core/file_sys/archive_extsavedata.h"
|
||||
@ -84,7 +84,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(cons
|
||||
ErrorSummary::InvalidState, ErrorLevel::Status);
|
||||
}
|
||||
}
|
||||
auto archive = Common::make_unique<DiskArchive>(fullpath);
|
||||
auto archive = std::make_unique<DiskArchive>(fullpath);
|
||||
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/make_unique.h"
|
||||
|
||||
#include "core/file_sys/archive_romfs.h"
|
||||
#include "core/file_sys/ivfc_archive.h"
|
||||
@ -25,7 +24,7 @@ ArchiveFactory_RomFS::ArchiveFactory_RomFS(Loader::AppLoader& app_loader) {
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_RomFS::Open(const Path& path) {
|
||||
auto archive = Common::make_unique<IVFCArchive>(romfs_file, data_offset, data_size);
|
||||
auto archive = std::make_unique<IVFCArchive>(romfs_file, data_offset, data_size);
|
||||
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
|
||||
}
|
||||
|
||||
|
@ -3,11 +3,11 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/make_unique.h"
|
||||
#include "common/string_util.h"
|
||||
|
||||
#include "core/file_sys/archive_savedata.h"
|
||||
@ -53,7 +53,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const P
|
||||
ErrorSummary::InvalidState, ErrorLevel::Status);
|
||||
}
|
||||
|
||||
auto archive = Common::make_unique<DiskArchive>(std::move(concrete_mount_point));
|
||||
auto archive = std::make_unique<DiskArchive>(std::move(concrete_mount_point));
|
||||
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
|
||||
}
|
||||
|
||||
|
@ -3,12 +3,12 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/make_unique.h"
|
||||
#include "common/string_util.h"
|
||||
|
||||
#include "core/file_sys/archive_savedatacheck.h"
|
||||
@ -44,7 +44,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveDataCheck::Open(co
|
||||
}
|
||||
auto size = file->GetSize();
|
||||
|
||||
auto archive = Common::make_unique<IVFCArchive>(file, 0, size);
|
||||
auto archive = std::make_unique<IVFCArchive>(file, 0, size);
|
||||
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
|
||||
}
|
||||
|
||||
|
@ -3,10 +3,10 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/make_unique.h"
|
||||
|
||||
#include "core/file_sys/archive_sdmc.h"
|
||||
#include "core/file_sys/disk_archive.h"
|
||||
@ -36,7 +36,7 @@ bool ArchiveFactory_SDMC::Initialize() {
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMC::Open(const Path& path) {
|
||||
auto archive = Common::make_unique<DiskArchive>(sdmc_directory);
|
||||
auto archive = std::make_unique<DiskArchive>(sdmc_directory);
|
||||
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
|
||||
}
|
||||
|
||||
|
@ -3,11 +3,11 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/make_unique.h"
|
||||
#include "common/string_util.h"
|
||||
|
||||
#include "core/file_sys/archive_systemsavedata.h"
|
||||
@ -59,7 +59,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SystemSaveData::Open(c
|
||||
return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS,
|
||||
ErrorSummary::InvalidState, ErrorLevel::Status);
|
||||
}
|
||||
auto archive = Common::make_unique<DiskArchive>(fullpath);
|
||||
auto archive = std::make_unique<DiskArchive>(fullpath);
|
||||
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
|
||||
}
|
||||
|
||||
|
@ -4,11 +4,11 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/make_unique.h"
|
||||
|
||||
#include "core/file_sys/disk_archive.h"
|
||||
|
||||
@ -19,7 +19,7 @@ namespace FileSys {
|
||||
|
||||
ResultVal<std::unique_ptr<FileBackend>> DiskArchive::OpenFile(const Path& path, const Mode mode) const {
|
||||
LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
|
||||
auto file = Common::make_unique<DiskFile>(*this, path, mode);
|
||||
auto file = std::make_unique<DiskFile>(*this, path, mode);
|
||||
ResultCode result = file->Open();
|
||||
if (result.IsError())
|
||||
return result;
|
||||
@ -83,7 +83,7 @@ bool DiskArchive::RenameDirectory(const Path& src_path, const Path& dest_path) c
|
||||
|
||||
std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) const {
|
||||
LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str());
|
||||
auto directory = Common::make_unique<DiskDirectory>(*this, path);
|
||||
auto directory = std::make_unique<DiskDirectory>(*this, path);
|
||||
if (!directory->Open())
|
||||
return nullptr;
|
||||
return std::move(directory);
|
||||
@ -132,7 +132,7 @@ ResultCode DiskFile::Open() {
|
||||
// Open the file in binary mode, to avoid problems with CR/LF on Windows systems
|
||||
mode_string += "b";
|
||||
|
||||
file = Common::make_unique<FileUtil::IOFile>(path, mode_string.c_str());
|
||||
file = std::make_unique<FileUtil::IOFile>(path, mode_string.c_str());
|
||||
if (file->IsOpen())
|
||||
return RESULT_SUCCESS;
|
||||
return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound, ErrorLevel::Status);
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/make_unique.h"
|
||||
|
||||
#include "core/file_sys/ivfc_archive.h"
|
||||
|
||||
@ -21,7 +20,7 @@ std::string IVFCArchive::GetName() const {
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path, const Mode mode) const {
|
||||
return MakeResult<std::unique_ptr<FileBackend>>(Common::make_unique<IVFCFile>(romfs_file, data_offset, data_size));
|
||||
return MakeResult<std::unique_ptr<FileBackend>>(std::make_unique<IVFCFile>(romfs_file, data_offset, data_size));
|
||||
}
|
||||
|
||||
ResultCode IVFCArchive::DeleteFile(const Path& path) const {
|
||||
@ -58,7 +57,7 @@ bool IVFCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) c
|
||||
}
|
||||
|
||||
std::unique_ptr<DirectoryBackend> IVFCArchive::OpenDirectory(const Path& path) const {
|
||||
return Common::make_unique<IVFCDirectory>();
|
||||
return std::make_unique<IVFCDirectory>();
|
||||
}
|
||||
|
||||
u64 IVFCArchive::GetFreeBytes() const {
|
||||
|
@ -60,6 +60,59 @@ const u32 R15_REGISTER = 15;
|
||||
const u32 CPSR_REGISTER = 25;
|
||||
const u32 FPSCR_REGISTER = 58;
|
||||
|
||||
// For sample XML files see the GDB source /gdb/features
|
||||
// GDB also wants the l character at the start
|
||||
// This XML defines what the registers are for this specific ARM device
|
||||
static const char* target_xml =
|
||||
R"(l<?xml version="1.0"?>
|
||||
<!DOCTYPE target SYSTEM "gdb-target.dtd">
|
||||
<target version="1.0">
|
||||
<feature name="org.gnu.gdb.arm.core">
|
||||
<reg name="r0" bitsize="32"/>
|
||||
<reg name="r1" bitsize="32"/>
|
||||
<reg name="r2" bitsize="32"/>
|
||||
<reg name="r3" bitsize="32"/>
|
||||
<reg name="r4" bitsize="32"/>
|
||||
<reg name="r5" bitsize="32"/>
|
||||
<reg name="r6" bitsize="32"/>
|
||||
<reg name="r7" bitsize="32"/>
|
||||
<reg name="r8" bitsize="32"/>
|
||||
<reg name="r9" bitsize="32"/>
|
||||
<reg name="r10" bitsize="32"/>
|
||||
<reg name="r11" bitsize="32"/>
|
||||
<reg name="r12" bitsize="32"/>
|
||||
<reg name="sp" bitsize="32" type="data_ptr"/>
|
||||
<reg name="lr" bitsize="32"/>
|
||||
<reg name="pc" bitsize="32" type="code_ptr"/>
|
||||
|
||||
<!-- The CPSR is register 25, rather than register 16, because
|
||||
the FPA registers historically were placed between the PC
|
||||
and the CPSR in the "g" packet. -->
|
||||
|
||||
<reg name="cpsr" bitsize="32" regnum="25"/>
|
||||
</feature>
|
||||
<feature name="org.gnu.gdb.arm.vfp">
|
||||
<reg name="d0" bitsize="64" type="float"/>
|
||||
<reg name="d1" bitsize="64" type="float"/>
|
||||
<reg name="d2" bitsize="64" type="float"/>
|
||||
<reg name="d3" bitsize="64" type="float"/>
|
||||
<reg name="d4" bitsize="64" type="float"/>
|
||||
<reg name="d5" bitsize="64" type="float"/>
|
||||
<reg name="d6" bitsize="64" type="float"/>
|
||||
<reg name="d7" bitsize="64" type="float"/>
|
||||
<reg name="d8" bitsize="64" type="float"/>
|
||||
<reg name="d9" bitsize="64" type="float"/>
|
||||
<reg name="d10" bitsize="64" type="float"/>
|
||||
<reg name="d11" bitsize="64" type="float"/>
|
||||
<reg name="d12" bitsize="64" type="float"/>
|
||||
<reg name="d13" bitsize="64" type="float"/>
|
||||
<reg name="d14" bitsize="64" type="float"/>
|
||||
<reg name="d15" bitsize="64" type="float"/>
|
||||
<reg name="fpscr" bitsize="32" type="int" group="float"/>
|
||||
</feature>
|
||||
</target>
|
||||
)";
|
||||
|
||||
namespace GDBStub {
|
||||
|
||||
static int gdbserver_socket = -1;
|
||||
@ -211,7 +264,7 @@ static u8 ReadByte() {
|
||||
}
|
||||
|
||||
/// Calculate the checksum of the current command buffer.
|
||||
static u8 CalculateChecksum(u8 *buffer, u32 length) {
|
||||
static u8 CalculateChecksum(u8* buffer, u32 length) {
|
||||
return static_cast<u8>(std::accumulate(buffer, buffer + length, 0, std::plus<u8>()));
|
||||
}
|
||||
|
||||
@ -353,8 +406,15 @@ static void SendReply(const char* reply) {
|
||||
static void HandleQuery() {
|
||||
LOG_DEBUG(Debug_GDBStub, "gdb: query '%s'\n", command_buffer + 1);
|
||||
|
||||
if (!strcmp(reinterpret_cast<const char*>(command_buffer + 1), "TStatus")) {
|
||||
const char* query = reinterpret_cast<const char*>(command_buffer + 1);
|
||||
|
||||
if (strcmp(query, "TStatus") == 0 ) {
|
||||
SendReply("T0");
|
||||
} else if (strncmp(query, "Supported:", strlen("Supported:")) == 0) {
|
||||
// PacketSize needs to be large enough for target xml
|
||||
SendReply("PacketSize=800;qXfer:features:read+");
|
||||
} else if (strncmp(query, "Xfer:features:read:target.xml:", strlen("Xfer:features:read:target.xml:")) == 0) {
|
||||
SendReply(target_xml);
|
||||
} else {
|
||||
SendReply("");
|
||||
}
|
||||
@ -469,7 +529,7 @@ static void ReadRegister() {
|
||||
id |= HexCharToValue(command_buffer[2]);
|
||||
}
|
||||
|
||||
if (id >= R0_REGISTER && id <= R15_REGISTER) {
|
||||
if (id <= R15_REGISTER) {
|
||||
IntToGdbHex(reply, Core::g_app_core->GetReg(id));
|
||||
} else if (id == CPSR_REGISTER) {
|
||||
IntToGdbHex(reply, Core::g_app_core->GetCPSR());
|
||||
@ -491,29 +551,25 @@ static void ReadRegisters() {
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
|
||||
u8* bufptr = buffer;
|
||||
for (int i = 0, reg = 0; reg <= FPSCR_REGISTER; i++, reg++) {
|
||||
if (reg <= R15_REGISTER) {
|
||||
IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetReg(reg));
|
||||
} else if (reg == CPSR_REGISTER) {
|
||||
IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetCPSR());
|
||||
} else if (reg == CPSR_REGISTER - 1) {
|
||||
// Dummy FPA register, ignore
|
||||
IntToGdbHex(bufptr + i * CHAR_BIT, 0);
|
||||
} else if (reg < CPSR_REGISTER) {
|
||||
// Dummy FPA registers, ignore
|
||||
IntToGdbHex(bufptr + i * CHAR_BIT, 0);
|
||||
IntToGdbHex(bufptr + (i + 1) * CHAR_BIT, 0);
|
||||
IntToGdbHex(bufptr + (i + 2) * CHAR_BIT, 0);
|
||||
i += 2;
|
||||
} else if (reg > CPSR_REGISTER && reg < FPSCR_REGISTER) {
|
||||
IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetVFPReg(reg - CPSR_REGISTER - 1));
|
||||
IntToGdbHex(bufptr + (i + 1) * CHAR_BIT, 0);
|
||||
i++;
|
||||
} else if (reg == FPSCR_REGISTER) {
|
||||
IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetVFPSystemReg(VFP_FPSCR));
|
||||
}
|
||||
|
||||
for (int reg = 0; reg <= R15_REGISTER; reg++) {
|
||||
IntToGdbHex(bufptr + reg * CHAR_BIT, Core::g_app_core->GetReg(reg));
|
||||
}
|
||||
|
||||
bufptr += (16 * CHAR_BIT);
|
||||
|
||||
IntToGdbHex(bufptr, Core::g_app_core->GetCPSR());
|
||||
|
||||
bufptr += CHAR_BIT;
|
||||
|
||||
for (int reg = 0; reg <= 31; reg++) {
|
||||
IntToGdbHex(bufptr + reg * CHAR_BIT, Core::g_app_core->GetVFPReg(reg));
|
||||
}
|
||||
|
||||
bufptr += (32 * CHAR_BIT);
|
||||
|
||||
IntToGdbHex(bufptr, Core::g_app_core->GetVFPSystemReg(VFP_FPSCR));
|
||||
|
||||
SendReply(reinterpret_cast<char*>(buffer));
|
||||
}
|
||||
|
||||
@ -528,7 +584,7 @@ static void WriteRegister() {
|
||||
id |= HexCharToValue(command_buffer[2]);
|
||||
}
|
||||
|
||||
if (id >= R0_REGISTER && id <= R15_REGISTER) {
|
||||
if (id <= R15_REGISTER) {
|
||||
Core::g_app_core->SetReg(id, GdbHexToInt(buffer_ptr));
|
||||
} else if (id == CPSR_REGISTER) {
|
||||
Core::g_app_core->SetCPSR(GdbHexToInt(buffer_ptr));
|
||||
@ -885,6 +941,12 @@ void Init(u16 port) {
|
||||
LOG_ERROR(Debug_GDBStub, "Failed to create gdb socket");
|
||||
}
|
||||
|
||||
// Set socket to SO_REUSEADDR so it can always bind on the same port
|
||||
int reuse_enabled = 1;
|
||||
if (setsockopt(tmpsock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_enabled, sizeof(reuse_enabled)) < 0) {
|
||||
LOG_ERROR(Debug_GDBStub, "Failed to set gdb socket option");
|
||||
}
|
||||
|
||||
const sockaddr* server_addr = reinterpret_cast<const sockaddr*>(&saddr_server);
|
||||
socklen_t server_addrlen = sizeof(saddr_server);
|
||||
if (bind(tmpsock, server_addr, server_addrlen) < 0) {
|
||||
|
@ -3,13 +3,6 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/common_funcs.h"
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/hle/config_mem.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -8,8 +8,6 @@
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/hle.h"
|
||||
#include "core/hle/config_mem.h"
|
||||
#include "core/hle/shared_page.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -2,10 +2,11 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/make_unique.h"
|
||||
|
||||
#include "core/hle/kernel/memory.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
|
@ -18,6 +18,7 @@
|
||||
/// Detailed description of the error. This listing is likely incomplete.
|
||||
enum class ErrorDescription : u32 {
|
||||
Success = 0,
|
||||
OS_InvalidBufferDescriptor = 48,
|
||||
WrongAddress = 53,
|
||||
FS_NotFound = 120,
|
||||
FS_AlreadyExists = 190,
|
||||
|
@ -2,6 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
@ -9,30 +11,119 @@
|
||||
#include "core/hle/service/am/am_app.h"
|
||||
#include "core/hle/service/am/am_net.h"
|
||||
#include "core/hle/service/am/am_sys.h"
|
||||
#include "core/hle/service/am/am_u.h"
|
||||
|
||||
namespace Service {
|
||||
namespace AM {
|
||||
|
||||
void TitleIDListGetTotal(Service::Interface* self) {
|
||||
static std::array<u32, 3> am_content_count = { 0, 0, 0 };
|
||||
static std::array<u32, 3> am_titles_count = { 0, 0, 0 };
|
||||
static std::array<u32, 3> am_titles_list_count = { 0, 0, 0 };
|
||||
static u32 am_ticket_count = 0;
|
||||
static u32 am_ticket_list_count = 0;
|
||||
|
||||
void GetTitleCount(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
u32 media_type = cmd_buff[1] & 0xFF;
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
cmd_buff[2] = 0;
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) media_type %u", media_type);
|
||||
cmd_buff[2] = am_titles_count[media_type];
|
||||
LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, title_count=0x%08x", media_type, am_titles_count[media_type]);
|
||||
}
|
||||
|
||||
void GetTitleIDList(Service::Interface* self) {
|
||||
void FindContentInfos(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
u32 num_titles = cmd_buff[1];
|
||||
u32 media_type = cmd_buff[2] & 0xFF;
|
||||
u32 addr = cmd_buff[4];
|
||||
|
||||
u32 media_type = cmd_buff[1] & 0xFF;
|
||||
u64 title_id = (static_cast<u64>(cmd_buff[3]) << 32) | cmd_buff[2];
|
||||
u32 content_ids_pointer = cmd_buff[6];
|
||||
u32 content_info_pointer = cmd_buff[8];
|
||||
|
||||
am_content_count[media_type] = cmd_buff[4];
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
cmd_buff[2] = 0;
|
||||
LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, title_id=0x%016llx, content_cound=%u, content_ids_pointer=0x%08x, content_info_pointer=0x%08x",
|
||||
media_type, title_id, am_content_count[media_type], content_ids_pointer, content_info_pointer);
|
||||
}
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) Requested %u titles from media type %u. Address=0x%08X", num_titles, media_type, addr);
|
||||
void ListContentInfos(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
u32 media_type = cmd_buff[2] & 0xFF;
|
||||
u64 title_id = (static_cast<u64>(cmd_buff[4]) << 32) | cmd_buff[3];
|
||||
u32 start_index = cmd_buff[5];
|
||||
u32 content_info_pointer = cmd_buff[7];
|
||||
|
||||
am_content_count[media_type] = cmd_buff[1];
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
cmd_buff[2] = am_content_count[media_type];
|
||||
LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, content_count=%u, title_id=0x%016" PRIx64 ", start_index=0x%08x, content_info_pointer=0x%08X",
|
||||
media_type, am_content_count[media_type], title_id, start_index, content_info_pointer);
|
||||
}
|
||||
|
||||
void DeleteContents(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
u32 media_type = cmd_buff[1] & 0xFF;
|
||||
u64 title_id = (static_cast<u64>(cmd_buff[3]) << 32) | cmd_buff[2];
|
||||
u32 content_ids_pointer = cmd_buff[6];
|
||||
|
||||
am_content_count[media_type] = cmd_buff[4];
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, title_id=0x%016" PRIx64 ", content_count=%u, content_ids_pointer=0x%08x",
|
||||
media_type, title_id, am_content_count[media_type], content_ids_pointer);
|
||||
}
|
||||
|
||||
void GetTitleList(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
u32 media_type = cmd_buff[2] & 0xFF;
|
||||
u32 title_ids_output_pointer = cmd_buff[4];
|
||||
|
||||
am_titles_list_count[media_type] = cmd_buff[1];
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
cmd_buff[2] = am_titles_list_count[media_type];
|
||||
LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, titles_list_count=0x%08X, title_ids_output_pointer=0x%08X",
|
||||
media_type, am_titles_list_count[media_type], title_ids_output_pointer);
|
||||
}
|
||||
|
||||
void GetTitleInfo(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
u32 media_type = cmd_buff[1] & 0xFF;
|
||||
u32 title_id_list_pointer = cmd_buff[4];
|
||||
u32 title_list_pointer = cmd_buff[6];
|
||||
|
||||
am_titles_count[media_type] = cmd_buff[2];
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, total_titles=0x%08X, title_id_list_pointer=0x%08X, title_list_pointer=0x%08X",
|
||||
media_type, am_titles_count[media_type], title_id_list_pointer, title_list_pointer);
|
||||
}
|
||||
|
||||
void GetDataTitleInfos(Service::Interface* self) {
|
||||
GetTitleInfo(self);
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void ListDataTitleTicketInfos(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
u64 title_id = (static_cast<u64>(cmd_buff[3]) << 32) | cmd_buff[2];
|
||||
u32 start_index = cmd_buff[4];
|
||||
u32 ticket_info_pointer = cmd_buff[6];
|
||||
|
||||
am_ticket_count = cmd_buff[1];
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
cmd_buff[2] = am_ticket_count;
|
||||
LOG_WARNING(Service_AM, "(STUBBED) ticket_count=0x%08X, title_id=0x%016" PRIx64 ", start_index=0x%08X, ticket_info_pointer=0x%08X",
|
||||
am_ticket_count, title_id, start_index, ticket_info_pointer);
|
||||
}
|
||||
|
||||
void GetNumContentInfos(Service::Interface* self) {
|
||||
@ -40,16 +131,47 @@ void GetNumContentInfos(Service::Interface* self) {
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
cmd_buff[2] = 1; // Number of content infos plus one
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void DeleteTicket(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
u64 title_id = (static_cast<u64>(cmd_buff[2]) << 32) | cmd_buff[1];
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called title_id=0x%016" PRIx64 "",title_id);
|
||||
}
|
||||
|
||||
void GetTicketCount(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
cmd_buff[2] = am_ticket_count;
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called ticket_count=0x%08x",am_ticket_count);
|
||||
}
|
||||
|
||||
void GetTicketList(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
u32 num_of_skip = cmd_buff[2];
|
||||
u32 ticket_list_pointer = cmd_buff[4];
|
||||
|
||||
am_ticket_list_count = cmd_buff[1];
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
cmd_buff[2] = am_ticket_list_count;
|
||||
LOG_WARNING(Service_AM, "(STUBBED) ticket_list_count=0x%08x, num_of_skip=0x%08x, ticket_list_pointer=0x%08x",
|
||||
am_ticket_list_count, num_of_skip, ticket_list_pointer);
|
||||
}
|
||||
|
||||
void Init() {
|
||||
using namespace Kernel;
|
||||
|
||||
AddService(new AM_APP_Interface);
|
||||
AddService(new AM_NET_Interface);
|
||||
AddService(new AM_SYS_Interface);
|
||||
AddService(new AM_U_Interface);
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
|
@ -11,7 +11,7 @@ class Interface;
|
||||
namespace AM {
|
||||
|
||||
/**
|
||||
* AM::TitleIDListGetTotal service function
|
||||
* AM::GetTitleCount service function
|
||||
* Gets the number of installed titles in the requested media type
|
||||
* Inputs:
|
||||
* 0 : Command header (0x00010040)
|
||||
@ -20,36 +20,140 @@ namespace AM {
|
||||
* 1 : Result, 0 on success, otherwise error code
|
||||
* 2 : The number of titles in the requested media type
|
||||
*/
|
||||
void TitleIDListGetTotal(Service::Interface* self);
|
||||
void GetTitleCount(Service::Interface* self);
|
||||
|
||||
/**
|
||||
* AM::GetTitleIDList service function
|
||||
* AM::FindContentInfos service function
|
||||
* Inputs:
|
||||
* 1 : MediaType
|
||||
* 2-3 : u64, Title ID
|
||||
* 4 : Content count
|
||||
* 6 : Content IDs pointer
|
||||
* 8 : Content Infos pointer
|
||||
* Outputs:
|
||||
* 1 : Result, 0 on success, otherwise error code
|
||||
*/
|
||||
void FindContentInfos(Service::Interface* self);
|
||||
|
||||
/**
|
||||
* AM::ListContentInfos service function
|
||||
* Inputs:
|
||||
* 1 : Content count
|
||||
* 2 : MediaType
|
||||
* 3-4 : u64, Title ID
|
||||
* 5 : Start Index
|
||||
* 7 : Content Infos pointer
|
||||
* Outputs:
|
||||
* 1 : Result, 0 on success, otherwise error code
|
||||
* 2 : Number of content infos returned
|
||||
*/
|
||||
void ListContentInfos(Service::Interface* self);
|
||||
|
||||
/**
|
||||
* AM::DeleteContents service function
|
||||
* Inputs:
|
||||
* 1 : MediaType
|
||||
* 2-3 : u64, Title ID
|
||||
* 4 : Content count
|
||||
* 6 : Content IDs pointer
|
||||
* Outputs:
|
||||
* 1 : Result, 0 on success, otherwise error code
|
||||
*/
|
||||
void DeleteContents(Service::Interface* self);
|
||||
|
||||
/**
|
||||
* AM::GetTitleList service function
|
||||
* Loads information about the desired number of titles from the desired media type into an array
|
||||
* Inputs:
|
||||
* 0 : Command header (0x00020082)
|
||||
* 1 : The maximum number of titles to load
|
||||
* 1 : Title count
|
||||
* 2 : Media type to load the titles from
|
||||
* 3 : Descriptor of the output buffer pointer
|
||||
* 4 : Address of the output buffer
|
||||
* 4 : Title IDs output pointer
|
||||
* Outputs:
|
||||
* 1 : Result, 0 on success, otherwise error code
|
||||
* 2 : The number of titles loaded from the requested media type
|
||||
*/
|
||||
void GetTitleIDList(Service::Interface* self);
|
||||
void GetTitleList(Service::Interface* self);
|
||||
|
||||
/**
|
||||
* AM::GetTitleInfo service function
|
||||
* Inputs:
|
||||
* 1 : u8 Mediatype
|
||||
* 2 : Total titles
|
||||
* 4 : TitleIDList pointer
|
||||
* 6 : TitleList pointer
|
||||
* Outputs:
|
||||
* 1 : Result, 0 on success, otherwise error code
|
||||
*/
|
||||
void GetTitleInfo(Service::Interface* self);
|
||||
|
||||
/**
|
||||
* AM::GetDataTitleInfos service function
|
||||
* Wrapper for AM::GetTitleInfo
|
||||
* Inputs:
|
||||
* 1 : u8 Mediatype
|
||||
* 2 : Total titles
|
||||
* 4 : TitleIDList pointer
|
||||
* 6 : TitleList pointer
|
||||
* Outputs:
|
||||
* 1 : Result, 0 on success, otherwise error code
|
||||
*/
|
||||
void GetDataTitleInfos(Service::Interface* self);
|
||||
|
||||
/**
|
||||
* AM::ListDataTitleTicketInfos service function
|
||||
* Inputs:
|
||||
* 1 : Ticket count
|
||||
* 2-3 : u64, Title ID
|
||||
* 4 : Start Index?
|
||||
* 5 : (TicketCount * 24) << 8 | 0x4
|
||||
* 6 : Ticket Infos pointer
|
||||
* Outputs:
|
||||
* 1 : Result, 0 on success, otherwise error code
|
||||
* 2 : Number of ticket infos returned
|
||||
*/
|
||||
void ListDataTitleTicketInfos(Service::Interface* self);
|
||||
|
||||
/**
|
||||
* AM::GetNumContentInfos service function
|
||||
* Inputs:
|
||||
* 0 : Command header (0x100100C0)
|
||||
* 1 : Unknown
|
||||
* 2 : Unknown
|
||||
* 3 : Unknown
|
||||
* 1 : MediaType
|
||||
* 2-3 : u64, Title ID
|
||||
* Outputs:
|
||||
* 1 : Result, 0 on success, otherwise error code
|
||||
* 2 : Number of content infos plus one
|
||||
*/
|
||||
void GetNumContentInfos(Service::Interface* self);
|
||||
|
||||
/**
|
||||
* AM::DeleteTicket service function
|
||||
* Inputs:
|
||||
* 1-2 : u64, Title ID
|
||||
* Outputs:
|
||||
* 1 : Result, 0 on success, otherwise error code
|
||||
*/
|
||||
void DeleteTicket(Service::Interface* self);
|
||||
|
||||
/**
|
||||
* AM::GetTicketCount service function
|
||||
* Outputs:
|
||||
* 1 : Result, 0 on success, otherwise error code
|
||||
* 2 : Total titles
|
||||
*/
|
||||
void GetTicketCount(Service::Interface* self);
|
||||
|
||||
/**
|
||||
* AM::GetTicketList service function
|
||||
* Inputs:
|
||||
* 1 : Number of TicketList
|
||||
* 2 : Number to skip
|
||||
* 4 : TicketList pointer
|
||||
* Outputs:
|
||||
* 1 : Result, 0 on success, otherwise error code
|
||||
* 2 : Total TicketList
|
||||
*/
|
||||
void GetTicketList(Service::Interface* self);
|
||||
|
||||
/// Initialize AM service
|
||||
void Init();
|
||||
|
||||
|
@ -9,14 +9,14 @@ namespace Service {
|
||||
namespace AM {
|
||||
|
||||
const Interface::FunctionInfo FunctionTable[] = {
|
||||
{0x100100C0, GetNumContentInfos, "GetNumContentInfos"},
|
||||
{0x10020104, nullptr, "FindContentInfos"},
|
||||
{0x10030142, nullptr, "ListContentInfos"},
|
||||
{0x10040102, nullptr, "DeleteContents"},
|
||||
{0x10050084, nullptr, "GetDataTitleInfos"},
|
||||
{0x10070102, nullptr, "ListDataTitleTicketInfos"},
|
||||
{0x100900C0, nullptr, "IsDataTitleInUse"},
|
||||
{0x100A0000, nullptr, "IsExternalTitleDatabaseInitialized"},
|
||||
{0x100100C0, GetNumContentInfos, "GetNumContentInfos"},
|
||||
{0x10020104, FindContentInfos, "FindContentInfos"},
|
||||
{0x10030142, ListContentInfos, "ListContentInfos"},
|
||||
{0x10040102, DeleteContents, "DeleteContents"},
|
||||
{0x10050084, GetDataTitleInfos, "GetDataTitleInfos"},
|
||||
{0x10070102, ListDataTitleTicketInfos, "ListDataTitleTicketInfos"},
|
||||
{0x100900C0, nullptr, "IsDataTitleInUse"},
|
||||
{0x100A0000, nullptr, "IsExternalTitleDatabaseInitialized"},
|
||||
};
|
||||
|
||||
AM_APP_Interface::AM_APP_Interface() {
|
||||
|
@ -9,16 +9,20 @@ namespace Service {
|
||||
namespace AM {
|
||||
|
||||
const Interface::FunctionInfo FunctionTable[] = {
|
||||
{0x00010040, TitleIDListGetTotal, "TitleIDListGetTotal"},
|
||||
{0x00020082, GetTitleIDList, "GetTitleIDList"},
|
||||
{0x00030084, nullptr, "ListTitles"},
|
||||
{0x00010040, GetTitleCount, "GetTitleCount"},
|
||||
{0x00020082, GetTitleList, "GetTitleList"},
|
||||
{0x00030084, GetTitleInfo, "GetTitleInfo"},
|
||||
{0x000400C0, nullptr, "DeleteApplicationTitle"},
|
||||
{0x000500C0, nullptr, "GetTitleProductCode"},
|
||||
{0x00080000, nullptr, "TitleIDListGetTotal3"},
|
||||
{0x00090082, nullptr, "GetTitleIDList3"},
|
||||
{0x000600C0, nullptr, "GetTitleExtDataId"},
|
||||
{0x00070080, DeleteTicket, "DeleteTicket"},
|
||||
{0x00080000, GetTicketCount, "GetTicketCount"},
|
||||
{0x00090082, GetTicketList, "GetTicketList"},
|
||||
{0x000A0000, nullptr, "GetDeviceID"},
|
||||
{0x000D0084, nullptr, "ListTitles2"},
|
||||
{0x00140040, nullptr, "FinishInstallToMedia"},
|
||||
{0x000D0084, nullptr, "GetPendingTitleInfo"},
|
||||
{0x000E00C0, nullptr, "DeletePendingTitle"},
|
||||
{0x00140040, nullptr, "FinalizePendingTitles"},
|
||||
{0x00150040, nullptr, "DeleteAllPendingTitles"},
|
||||
{0x00180080, nullptr, "InitializeTitleDatabase"},
|
||||
{0x00190040, nullptr, "ReloadDBS"},
|
||||
{0x001A00C0, nullptr, "GetDSiWareExportSize"},
|
||||
|
@ -9,23 +9,27 @@ namespace Service {
|
||||
namespace AM {
|
||||
|
||||
const Interface::FunctionInfo FunctionTable[] = {
|
||||
{0x00010040, TitleIDListGetTotal, "TitleIDListGetTotal"},
|
||||
{0x00020082, GetTitleIDList, "GetTitleIDList"},
|
||||
{0x00030084, nullptr, "ListTitles"},
|
||||
{0x00010040, GetTitleCount, "GetTitleCount"},
|
||||
{0x00020082, GetTitleList, "GetTitleList"},
|
||||
{0x00030084, GetTitleInfo, "GetTitleInfo"},
|
||||
{0x000400C0, nullptr, "DeleteApplicationTitle"},
|
||||
{0x000500C0, nullptr, "GetTitleProductCode"},
|
||||
{0x00080000, nullptr, "TitleIDListGetTotal3"},
|
||||
{0x00090082, nullptr, "GetTitleIDList3"},
|
||||
{0x000600C0, nullptr, "GetTitleExtDataId"},
|
||||
{0x00070080, DeleteTicket, "DeleteTicket"},
|
||||
{0x00080000, GetTicketCount, "GetTicketCount"},
|
||||
{0x00090082, GetTicketList, "GetTicketList"},
|
||||
{0x000A0000, nullptr, "GetDeviceID"},
|
||||
{0x000D0084, nullptr, "ListTitles2"},
|
||||
{0x00140040, nullptr, "FinishInstallToMedia"},
|
||||
{0x000D0084, nullptr, "GetPendingTitleInfo"},
|
||||
{0x000E00C0, nullptr, "DeletePendingTitle"},
|
||||
{0x00140040, nullptr, "FinalizePendingTitles"},
|
||||
{0x00150040, nullptr, "DeleteAllPendingTitles"},
|
||||
{0x00180080, nullptr, "InitializeTitleDatabase"},
|
||||
{0x00190040, nullptr, "ReloadDBS"},
|
||||
{0x001A00C0, nullptr, "GetDSiWareExportSize"},
|
||||
{0x001B0144, nullptr, "ExportDSiWare"},
|
||||
{0x001C0084, nullptr, "ImportDSiWare"},
|
||||
{0x00230080, nullptr, "TitleIDListGetTotal2"},
|
||||
{0x002400C2, nullptr, "GetTitleIDList2"}
|
||||
{0x00230080, nullptr, "GetPendingTitleCount"},
|
||||
{0x002400C2, nullptr, "GetPendingTitleList"}
|
||||
};
|
||||
|
||||
AM_SYS_Interface::AM_SYS_Interface() {
|
||||
|
@ -9,16 +9,20 @@ namespace Service {
|
||||
namespace AM {
|
||||
|
||||
const Interface::FunctionInfo FunctionTable[] = {
|
||||
{0x00010040, TitleIDListGetTotal, "TitleIDListGetTotal"},
|
||||
{0x00020082, GetTitleIDList, "GetTitleIDList"},
|
||||
{0x00030084, nullptr, "ListTitles"},
|
||||
{0x00010040, GetTitleCount, "GetTitleCount"},
|
||||
{0x00020082, GetTitleList, "GetTitleList"},
|
||||
{0x00030084, GetTitleInfo, "GetTitleInfo"},
|
||||
{0x000400C0, nullptr, "DeleteApplicationTitle"},
|
||||
{0x000500C0, nullptr, "GetTitleProductCode"},
|
||||
{0x00080000, nullptr, "TitleIDListGetTotal3"},
|
||||
{0x00090082, nullptr, "GetTitleIDList3"},
|
||||
{0x000600C0, nullptr, "GetTitleExtDataId"},
|
||||
{0x00070080, DeleteTicket, "DeleteTicket"},
|
||||
{0x00080000, GetTicketCount, "GetTicketCount"},
|
||||
{0x00090082, GetTicketList, "GetTicketList"},
|
||||
{0x000A0000, nullptr, "GetDeviceID"},
|
||||
{0x000D0084, nullptr, "ListTitles2"},
|
||||
{0x00140040, nullptr, "FinishInstallToMedia"},
|
||||
{0x000D0084, nullptr, "GetPendingTitleInfo"},
|
||||
{0x000E00C0, nullptr, "DeletePendingTitle"},
|
||||
{0x00140040, nullptr, "FinalizePendingTitles"},
|
||||
{0x00150040, nullptr, "DeleteAllPendingTitles"},
|
||||
{0x00180080, nullptr, "InitializeTitleDatabase"},
|
||||
{0x00190040, nullptr, "ReloadDBS"},
|
||||
{0x001A00C0, nullptr, "GetDSiWareExportSize"},
|
||||
|
@ -397,6 +397,23 @@ void GetAppletInfo(Service::Interface* self) {
|
||||
LOG_WARNING(Service_APT, "(stubbed) called appid=%u", app_id);
|
||||
}
|
||||
|
||||
void GetStartupArgument(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
u32 parameter_size = cmd_buff[1];
|
||||
StartupArgumentType startup_argument_type = static_cast<StartupArgumentType>(cmd_buff[2]);
|
||||
|
||||
if (parameter_size >= 0x300) {
|
||||
LOG_ERROR(Service_APT, "Parameter size is outside the valid range (capped to 0x300): parameter_size=0x%08x", parameter_size);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_WARNING(Service_APT,"(stubbed) called startup_argument_type=%u , parameter_size=0x%08x , parameter_value=0x%08x",
|
||||
startup_argument_type, parameter_size, Memory::Read32(cmd_buff[41]));
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
cmd_buff[2] = (parameter_size > 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
void Init() {
|
||||
AddService(new APT_A_Interface);
|
||||
AddService(new APT_S_Interface);
|
||||
|
@ -67,6 +67,12 @@ enum class AppletId : u32 {
|
||||
Ed2 = 0x402,
|
||||
};
|
||||
|
||||
enum class StartupArgumentType : u32 {
|
||||
OtherApp = 0,
|
||||
Restart = 1,
|
||||
OtherMedia = 2,
|
||||
};
|
||||
|
||||
/// Send a parameter to the currently-running application, which will read it via ReceiveParameter
|
||||
void SendParameter(const MessageParameter& parameter);
|
||||
|
||||
@ -344,6 +350,17 @@ void PreloadLibraryApplet(Service::Interface* self);
|
||||
*/
|
||||
void StartLibraryApplet(Service::Interface* self);
|
||||
|
||||
/**
|
||||
* APT::GetStartupArgument service function
|
||||
* Inputs:
|
||||
* 1 : Parameter Size (capped to 0x300)
|
||||
* 2 : StartupArgumentType
|
||||
* Outputs:
|
||||
* 0 : Return header
|
||||
* 1 : u8, Exists (0 = does not exist, 1 = exists)
|
||||
*/
|
||||
void GetStartupArgument(Service::Interface* self);
|
||||
|
||||
/// Initialize the APT service
|
||||
void Init();
|
||||
|
||||
|
@ -13,9 +13,10 @@ const Interface::FunctionInfo FunctionTable[] = {
|
||||
{0x00020080, Initialize, "Initialize?"},
|
||||
{0x00030040, Enable, "Enable?"},
|
||||
{0x00040040, nullptr, "Finalize?"},
|
||||
{0x00050040, nullptr, "GetAppletManInfo?"},
|
||||
{0x00060040, nullptr, "GetAppletInfo?"},
|
||||
{0x00050040, GetAppletManInfo, "GetAppletManInfo"},
|
||||
{0x00060040, GetAppletInfo, "GetAppletInfo"},
|
||||
{0x00090040, IsRegistered, "IsRegistered"},
|
||||
{0x000B0040, InquireNotification, "InquireNotification"},
|
||||
{0x000C0104, SendParameter, "SendParameter"},
|
||||
{0x000D0080, ReceiveParameter, "ReceiveParameter"},
|
||||
{0x000E0080, GlanceParameter, "GlanceParameter"},
|
||||
@ -24,9 +25,13 @@ const Interface::FunctionInfo FunctionTable[] = {
|
||||
{0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
|
||||
{0x001E0084, StartLibraryApplet, "StartLibraryApplet"},
|
||||
{0x003B0040, nullptr, "CancelLibraryApplet?"},
|
||||
{0x003E0080, nullptr, "ReplySleepQuery"},
|
||||
{0x00430040, NotifyToWait, "NotifyToWait?"},
|
||||
{0x00440000, GetSharedFont, "GetSharedFont?"},
|
||||
{0x004B00C2, AppletUtility, "AppletUtility?"},
|
||||
{0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"},
|
||||
{0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"},
|
||||
{0x00510080, GetStartupArgument, "GetStartupArgument"},
|
||||
{0x00550040, nullptr, "WriteInputToNsState?"},
|
||||
};
|
||||
|
||||
|
@ -13,8 +13,8 @@ const Interface::FunctionInfo FunctionTable[] = {
|
||||
{0x00020080, Initialize, "Initialize"},
|
||||
{0x00030040, Enable, "Enable"},
|
||||
{0x00040040, nullptr, "Finalize"},
|
||||
{0x00050040, nullptr, "GetAppletManInfo"},
|
||||
{0x00060040, nullptr, "GetAppletInfo"},
|
||||
{0x00050040, GetAppletManInfo, "GetAppletManInfo"},
|
||||
{0x00060040, GetAppletInfo, "GetAppletInfo"},
|
||||
{0x00070000, nullptr, "GetLastSignaledAppletId"},
|
||||
{0x00080000, nullptr, "CountRegisteredApplet"},
|
||||
{0x00090040, nullptr, "IsRegistered"},
|
||||
@ -87,9 +87,9 @@ const Interface::FunctionInfo FunctionTable[] = {
|
||||
{0x004C0000, nullptr, "SetFatalErrDispMode"},
|
||||
{0x004D0080, nullptr, "GetAppletProgramInfo"},
|
||||
{0x004E0000, nullptr, "HardwareResetAsync"},
|
||||
{0x004F0080, nullptr, "SetApplicationCpuTimeLimit"},
|
||||
{0x00500040, nullptr, "GetApplicationCpuTimeLimit"},
|
||||
{0x00510080, nullptr, "GetStartupArgument"},
|
||||
{0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"},
|
||||
{0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"},
|
||||
{0x00510080, GetStartupArgument, "GetStartupArgument"},
|
||||
{0x00520104, nullptr, "Wrap1"},
|
||||
{0x00530104, nullptr, "Unwrap1"},
|
||||
{0x00580002, nullptr, "GetProgramID"},
|
||||
|
@ -89,7 +89,7 @@ const Interface::FunctionInfo FunctionTable[] = {
|
||||
{0x004E0000, nullptr, "HardwareResetAsync"},
|
||||
{0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"},
|
||||
{0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"},
|
||||
{0x00510080, nullptr, "GetStartupArgument"},
|
||||
{0x00510080, GetStartupArgument, "GetStartupArgument"},
|
||||
{0x00520104, nullptr, "Wrap1"},
|
||||
{0x00530104, nullptr, "Unwrap1"},
|
||||
{0x00580002, nullptr, "GetProgramID"},
|
||||
|
@ -16,6 +16,15 @@ namespace CECD {
|
||||
static Kernel::SharedPtr<Kernel::Event> cecinfo_event;
|
||||
static Kernel::SharedPtr<Kernel::Event> change_state_event;
|
||||
|
||||
void GetCecStateAbbreviated(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
cmd_buff[2] = static_cast<u32>(CecStateAbbreviated::CEC_STATE_ABBREV_IDLE);
|
||||
|
||||
LOG_WARNING(Service_CECD, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void GetCecInfoEventHandle(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
|
@ -10,6 +10,24 @@ class Interface;
|
||||
|
||||
namespace CECD {
|
||||
|
||||
enum class CecStateAbbreviated {
|
||||
CEC_STATE_ABBREV_IDLE = 1, ///< Corresponds to CEC_STATE_IDLE
|
||||
CEC_STATE_ABBREV_NOT_LOCAL = 2, ///< Corresponds to CEC_STATEs *FINISH*, *POST, and OVER_BOSS
|
||||
CEC_STATE_ABBREV_SCANNING = 3, ///< Corresponds to CEC_STATE_SCANNING
|
||||
CEC_STATE_ABBREV_WLREADY = 4, ///< Corresponds to CEC_STATE_WIRELESS_READY when some unknown bool is true
|
||||
CEC_STATE_ABBREV_OTHER = 5, ///< Corresponds to CEC_STATEs besides *FINISH*, *POST, and OVER_BOSS and those listed here
|
||||
};
|
||||
|
||||
/**
|
||||
* GetCecStateAbbreviated service function
|
||||
* Inputs:
|
||||
* 0: 0x000E0000
|
||||
* Outputs:
|
||||
* 1: ResultCode
|
||||
* 2: CecStateAbbreviated
|
||||
*/
|
||||
void GetCecStateAbbreviated(Service::Interface* self);
|
||||
|
||||
/**
|
||||
* GetCecInfoEventHandle service function
|
||||
* Inputs:
|
||||
|
@ -9,6 +9,7 @@ namespace Service {
|
||||
namespace CECD {
|
||||
|
||||
static const Interface::FunctionInfo FunctionTable[] = {
|
||||
{0x000E0000, GetCecStateAbbreviated, "GetCecStateAbbreviated"},
|
||||
{0x000F0000, GetCecInfoEventHandle, "GetCecInfoEventHandle"},
|
||||
{0x00100000, GetChangeStateEventHandle, "GetChangeStateEventHandle"},
|
||||
{0x00120104, nullptr, "ReadSavedData"},
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
|
||||
#include "audio_core/hle/pipe.h"
|
||||
@ -12,37 +13,80 @@
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/service/dsp_dsp.h"
|
||||
|
||||
using DspPipe = DSP::HLE::DspPipe;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Namespace DSP_DSP
|
||||
|
||||
namespace DSP_DSP {
|
||||
|
||||
static u32 read_pipe_count;
|
||||
static Kernel::SharedPtr<Kernel::Event> semaphore_event;
|
||||
|
||||
struct PairHash {
|
||||
template <typename T, typename U>
|
||||
std::size_t operator()(const std::pair<T, U> &x) const {
|
||||
// TODO(yuriks): Replace with better hash combining function.
|
||||
return std::hash<T>()(x.first) ^ std::hash<U>()(x.second);
|
||||
/// There are three types of interrupts
|
||||
enum class InterruptType {
|
||||
Zero, One, Pipe
|
||||
};
|
||||
constexpr size_t NUM_INTERRUPT_TYPE = 3;
|
||||
|
||||
class InterruptEvents final {
|
||||
public:
|
||||
void Signal(InterruptType type, DspPipe pipe) {
|
||||
Kernel::SharedPtr<Kernel::Event>& event = Get(type, pipe);
|
||||
if (event) {
|
||||
event->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event>& Get(InterruptType type, DspPipe dsp_pipe) {
|
||||
switch (type) {
|
||||
case InterruptType::Zero:
|
||||
return zero;
|
||||
case InterruptType::One:
|
||||
return one;
|
||||
case InterruptType::Pipe: {
|
||||
const size_t pipe_index = static_cast<size_t>(dsp_pipe);
|
||||
ASSERT(pipe_index < DSP::HLE::NUM_DSP_PIPE);
|
||||
return pipe[pipe_index];
|
||||
}
|
||||
}
|
||||
|
||||
UNREACHABLE_MSG("Invalid interrupt type = %zu", static_cast<size_t>(type));
|
||||
}
|
||||
|
||||
bool HasTooManyEventsRegistered() const {
|
||||
// Actual service implementation only has 6 'slots' for interrupts.
|
||||
constexpr size_t max_number_of_interrupt_events = 6;
|
||||
|
||||
size_t number = std::count_if(pipe.begin(), pipe.end(), [](const auto& evt) {
|
||||
return evt != nullptr;
|
||||
});
|
||||
|
||||
if (zero != nullptr)
|
||||
number++;
|
||||
if (one != nullptr)
|
||||
number++;
|
||||
|
||||
return number >= max_number_of_interrupt_events;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Currently unknown purpose
|
||||
Kernel::SharedPtr<Kernel::Event> zero = nullptr;
|
||||
/// Currently unknown purpose
|
||||
Kernel::SharedPtr<Kernel::Event> one = nullptr;
|
||||
/// Each DSP pipe has an associated interrupt
|
||||
std::array<Kernel::SharedPtr<Kernel::Event>, DSP::HLE::NUM_DSP_PIPE> pipe = {{}};
|
||||
};
|
||||
|
||||
/// Map of (audio interrupt number, channel number) to Kernel::Events. See: RegisterInterruptEvents
|
||||
static std::unordered_map<std::pair<u32, u32>, Kernel::SharedPtr<Kernel::Event>, PairHash> interrupt_events;
|
||||
static InterruptEvents interrupt_events;
|
||||
|
||||
// DSP Interrupts:
|
||||
// Interrupt #2 occurs every frame tick. Userland programs normally have a thread that's waiting
|
||||
// for an interrupt event. Immediately after this interrupt event, userland normally updates the
|
||||
// state in the next region and increments the relevant frame counter by two.
|
||||
void SignalAllInterrupts() {
|
||||
// HACK: The other interrupts have currently unknown purpose, we trigger them each tick in any case.
|
||||
for (auto& interrupt_event : interrupt_events)
|
||||
interrupt_event.second->Signal();
|
||||
}
|
||||
|
||||
void SignalInterrupt(u32 interrupt, u32 channel) {
|
||||
interrupt_events[std::make_pair(interrupt, channel)]->Signal();
|
||||
// The audio-pipe interrupt occurs every frame tick. Userland programs normally have a thread
|
||||
// that's waiting for an interrupt event. Immediately after this interrupt event, userland
|
||||
// normally updates the state in the next region and increments the relevant frame counter by
|
||||
// two.
|
||||
void SignalPipeInterrupt(DspPipe pipe) {
|
||||
interrupt_events.Signal(InterruptType::Pipe, pipe);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -58,7 +102,10 @@ static void ConvertProcessAddressFromDspDram(Service::Interface* self) {
|
||||
|
||||
u32 addr = cmd_buff[1];
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0xC, 2, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
|
||||
// TODO(merry): There is a per-region offset missing in this calculation (that seems to be always zero).
|
||||
cmd_buff[2] = (addr << 1) + (Memory::DSP_RAM_VADDR + 0x40000);
|
||||
|
||||
LOG_DEBUG(Service_DSP, "addr=0x%08X", addr);
|
||||
@ -113,7 +160,9 @@ static void LoadComponent(Service::Interface* self) {
|
||||
static void GetSemaphoreEventHandle(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0x16, 1, 2);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
// cmd_buff[2] not set
|
||||
cmd_buff[3] = Kernel::g_handle_table.Create(semaphore_event).MoveFrom(); // Event handle
|
||||
|
||||
LOG_WARNING(Service_DSP, "(STUBBED) called");
|
||||
@ -138,8 +187,7 @@ static void FlushDataCache(Service::Interface* self) {
|
||||
u32 size = cmd_buff[2];
|
||||
u32 process = cmd_buff[4];
|
||||
|
||||
// TODO(purpasmart96): Verify return header on HW
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0x13, 1, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
|
||||
LOG_TRACE(Service_DSP, "called address=0x%08X, size=0x%X, process=0x%08X", address, size, process);
|
||||
@ -148,8 +196,8 @@ static void FlushDataCache(Service::Interface* self) {
|
||||
/**
|
||||
* DSP_DSP::RegisterInterruptEvents service function
|
||||
* Inputs:
|
||||
* 1 : Interrupt Number
|
||||
* 2 : Channel Number
|
||||
* 1 : Interrupt Type
|
||||
* 2 : Pipe Number
|
||||
* 4 : Interrupt event handle
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
@ -157,23 +205,40 @@ static void FlushDataCache(Service::Interface* self) {
|
||||
static void RegisterInterruptEvents(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
u32 interrupt = cmd_buff[1];
|
||||
u32 channel = cmd_buff[2];
|
||||
u32 type_index = cmd_buff[1];
|
||||
u32 pipe_index = cmd_buff[2];
|
||||
u32 event_handle = cmd_buff[4];
|
||||
|
||||
ASSERT_MSG(type_index < NUM_INTERRUPT_TYPE && pipe_index < DSP::HLE::NUM_DSP_PIPE,
|
||||
"Invalid type or pipe: type = %u, pipe = %u", type_index, pipe_index);
|
||||
|
||||
InterruptType type = static_cast<InterruptType>(cmd_buff[1]);
|
||||
DspPipe pipe = static_cast<DspPipe>(cmd_buff[2]);
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0x15, 1, 0);
|
||||
|
||||
if (event_handle) {
|
||||
auto evt = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]);
|
||||
if (evt) {
|
||||
interrupt_events[std::make_pair(interrupt, channel)] = evt;
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
LOG_INFO(Service_DSP, "Registered interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle);
|
||||
} else {
|
||||
LOG_CRITICAL(Service_DSP, "Invalid event handle! interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle);
|
||||
ASSERT(false); // This should really be handled at a IPC translation layer.
|
||||
|
||||
if (!evt) {
|
||||
LOG_INFO(Service_DSP, "Invalid event handle! type=%u, pipe=%u, event_handle=0x%08X", type_index, pipe_index, event_handle);
|
||||
ASSERT(false); // TODO: This should really be handled at an IPC translation layer.
|
||||
}
|
||||
|
||||
if (interrupt_events.HasTooManyEventsRegistered()) {
|
||||
LOG_INFO(Service_DSP, "Ran out of space to register interrupts (Attempted to register type=%u, pipe=%u, event_handle=0x%08X)",
|
||||
type_index, pipe_index, event_handle);
|
||||
cmd_buff[1] = ResultCode(ErrorDescription::InvalidResultValue, ErrorModule::DSP, ErrorSummary::OutOfResource, ErrorLevel::Status).raw;
|
||||
return;
|
||||
}
|
||||
|
||||
interrupt_events.Get(type, pipe) = evt;
|
||||
LOG_INFO(Service_DSP, "Registered type=%u, pipe=%u, event_handle=0x%08X", type_index, pipe_index, event_handle);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
} else {
|
||||
interrupt_events.erase(std::make_pair(interrupt, channel));
|
||||
LOG_INFO(Service_DSP, "Unregistered interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle);
|
||||
interrupt_events.Get(type, pipe) = nullptr;
|
||||
LOG_INFO(Service_DSP, "Unregistered interrupt=%u, channel=%u, event_handle=0x%08X", type_index, pipe_index, event_handle);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,6 +252,7 @@ static void RegisterInterruptEvents(Service::Interface* self) {
|
||||
static void SetSemaphore(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
|
||||
LOG_WARNING(Service_DSP, "(STUBBED) called");
|
||||
@ -195,7 +261,7 @@ static void SetSemaphore(Service::Interface* self) {
|
||||
/**
|
||||
* DSP_DSP::WriteProcessPipe service function
|
||||
* Inputs:
|
||||
* 1 : Channel
|
||||
* 1 : Pipe Number
|
||||
* 2 : Size
|
||||
* 3 : (size << 14) | 0x402
|
||||
* 4 : Buffer
|
||||
@ -206,24 +272,32 @@ static void SetSemaphore(Service::Interface* self) {
|
||||
static void WriteProcessPipe(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(cmd_buff[1]);
|
||||
u32 pipe_index = cmd_buff[1];
|
||||
u32 size = cmd_buff[2];
|
||||
u32 buffer = cmd_buff[4];
|
||||
|
||||
ASSERT_MSG(IPC::StaticBufferDesc(size, 1) == cmd_buff[3], "IPC static buffer descriptor failed validation (0x%X). pipe=%u, size=0x%X, buffer=0x%08X", cmd_buff[3], pipe, size, buffer);
|
||||
ASSERT_MSG(Memory::GetPointer(buffer) != nullptr, "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe, size, buffer);
|
||||
DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index);
|
||||
|
||||
if (IPC::StaticBufferDesc(size, 1) != cmd_buff[3]) {
|
||||
LOG_ERROR(Service_DSP, "IPC static buffer descriptor failed validation (0x%X). pipe=%u, size=0x%X, buffer=0x%08X", cmd_buff[3], pipe_index, size, buffer);
|
||||
cmd_buff[0] = IPC::MakeHeader(0, 1, 0);
|
||||
cmd_buff[1] = ResultCode(ErrorDescription::OS_InvalidBufferDescriptor, ErrorModule::OS, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw;
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT_MSG(Memory::GetPointer(buffer) != nullptr, "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe_index, size, buffer);
|
||||
|
||||
std::vector<u8> message(size);
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
message[i] = Memory::Read8(buffer + i);
|
||||
}
|
||||
|
||||
DSP::HLE::PipeWrite(pipe, message);
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0xD, 1, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
|
||||
LOG_DEBUG(Service_DSP, "pipe=%u, size=0x%X, buffer=0x%08X", pipe, size, buffer);
|
||||
LOG_DEBUG(Service_DSP, "pipe=%u, size=0x%X, buffer=0x%08X", pipe_index, size, buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -243,13 +317,16 @@ static void WriteProcessPipe(Service::Interface* self) {
|
||||
static void ReadPipeIfPossible(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(cmd_buff[1]);
|
||||
u32 pipe_index = cmd_buff[1];
|
||||
u32 unknown = cmd_buff[2];
|
||||
u32 size = cmd_buff[3] & 0xFFFF; // Lower 16 bits are size
|
||||
VAddr addr = cmd_buff[0x41];
|
||||
|
||||
ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, size, addr);
|
||||
DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index);
|
||||
|
||||
ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index, unknown, size, addr);
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0x10, 1, 2);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
if (DSP::HLE::GetPipeReadableSize(pipe) >= size) {
|
||||
std::vector<u8> response = DSP::HLE::PipeRead(pipe, size);
|
||||
@ -260,8 +337,10 @@ static void ReadPipeIfPossible(Service::Interface* self) {
|
||||
} else {
|
||||
cmd_buff[2] = 0; // Return no data
|
||||
}
|
||||
cmd_buff[3] = IPC::StaticBufferDesc(size, 0);
|
||||
cmd_buff[4] = addr;
|
||||
|
||||
LOG_DEBUG(Service_DSP, "pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", pipe, unknown, size, addr, cmd_buff[2]);
|
||||
LOG_DEBUG(Service_DSP, "pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", pipe_index, unknown, size, addr, cmd_buff[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -278,26 +357,31 @@ static void ReadPipeIfPossible(Service::Interface* self) {
|
||||
static void ReadPipe(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(cmd_buff[1]);
|
||||
u32 pipe_index = cmd_buff[1];
|
||||
u32 unknown = cmd_buff[2];
|
||||
u32 size = cmd_buff[3] & 0xFFFF; // Lower 16 bits are size
|
||||
VAddr addr = cmd_buff[0x41];
|
||||
|
||||
ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, size, addr);
|
||||
DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index);
|
||||
|
||||
ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index, unknown, size, addr);
|
||||
|
||||
if (DSP::HLE::GetPipeReadableSize(pipe) >= size) {
|
||||
std::vector<u8> response = DSP::HLE::PipeRead(pipe, size);
|
||||
|
||||
Memory::WriteBlock(addr, response.data(), response.size());
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0xE, 2, 2);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
cmd_buff[2] = static_cast<u32>(response.size());
|
||||
cmd_buff[3] = IPC::StaticBufferDesc(size, 0);
|
||||
cmd_buff[4] = addr;
|
||||
} else {
|
||||
// No more data is in pipe. Hardware hangs in this case; this should never happen.
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_DSP, "pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", pipe, unknown, size, addr, cmd_buff[2]);
|
||||
LOG_DEBUG(Service_DSP, "pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", pipe_index, unknown, size, addr, cmd_buff[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -312,13 +396,16 @@ static void ReadPipe(Service::Interface* self) {
|
||||
static void GetPipeReadableSize(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(cmd_buff[1]);
|
||||
u32 pipe_index = cmd_buff[1];
|
||||
u32 unknown = cmd_buff[2];
|
||||
|
||||
DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index);
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
cmd_buff[2] = DSP::HLE::GetPipeReadableSize(pipe);
|
||||
|
||||
LOG_DEBUG(Service_DSP, "pipe=0x%08X, unknown=0x%08X, return cmd_buff[2]=0x%08X", pipe, unknown, cmd_buff[2]);
|
||||
LOG_DEBUG(Service_DSP, "pipe=%u, unknown=0x%08X, return cmd_buff[2]=0x%08X", pipe_index, unknown, cmd_buff[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -333,6 +420,7 @@ static void SetSemaphoreMask(Service::Interface* self) {
|
||||
|
||||
u32 mask = cmd_buff[1];
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0x17, 1, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
|
||||
LOG_WARNING(Service_DSP, "(STUBBED) called mask=0x%08X", mask);
|
||||
@ -350,6 +438,7 @@ static void SetSemaphoreMask(Service::Interface* self) {
|
||||
static void GetHeadphoneStatus(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0x1F, 2, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
cmd_buff[2] = 0; // Not using headphones?
|
||||
|
||||
@ -376,6 +465,7 @@ static void RecvData(Service::Interface* self) {
|
||||
|
||||
// Application reads this after requesting DSP shutdown, to verify the DSP has indeed shutdown or slept.
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0x1, 2, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
switch (DSP::HLE::GetDspState()) {
|
||||
case DSP::HLE::DspState::On:
|
||||
@ -411,6 +501,7 @@ static void RecvDataIsReady(Service::Interface* self) {
|
||||
|
||||
ASSERT_MSG(register_number == 0, "Unknown register_number %u", register_number);
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0x2, 2, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
cmd_buff[2] = 1; // Ready to read
|
||||
|
||||
@ -458,14 +549,14 @@ const Interface::FunctionInfo FunctionTable[] = {
|
||||
|
||||
Interface::Interface() {
|
||||
semaphore_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "DSP_DSP::semaphore_event");
|
||||
read_pipe_count = 0;
|
||||
interrupt_events = {};
|
||||
|
||||
Register(FunctionTable);
|
||||
}
|
||||
|
||||
Interface::~Interface() {
|
||||
semaphore_event = nullptr;
|
||||
interrupt_events.clear();
|
||||
interrupt_events = {};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -8,6 +8,12 @@
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace DSP {
|
||||
namespace HLE {
|
||||
enum class DspPipe;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Namespace DSP_DSP
|
||||
|
||||
@ -23,15 +29,10 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/// Signal all audio related interrupts.
|
||||
void SignalAllInterrupts();
|
||||
|
||||
/**
|
||||
* Signal a specific audio related interrupt based on interrupt id and channel id.
|
||||
* @param interrupt_id The interrupt id
|
||||
* @param channel_id The channel id
|
||||
* The significance of various values of interrupt_id and channel_id is not yet known.
|
||||
* Signal a specific DSP related interrupt of type == InterruptType::Pipe, pipe == pipe.
|
||||
* @param pipe The DSP pipe for which to signal an interrupt for.
|
||||
*/
|
||||
void SignalInterrupt(u32 interrupt_id, u32 channel_id);
|
||||
void SignalPipeInterrupt(DSP::HLE::DspPipe pipe);
|
||||
|
||||
} // namespace
|
||||
} // namespace DSP_DSP
|
||||
|
@ -2,6 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/string_util.h"
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/frd/frd.h"
|
||||
#include "core/hle/service/frd/frd_a.h"
|
||||
@ -10,6 +12,95 @@
|
||||
namespace Service {
|
||||
namespace FRD {
|
||||
|
||||
static FriendKey my_friend_key = {0, 0, 0ull};
|
||||
static MyPresence my_presence = {};
|
||||
|
||||
void GetMyPresence(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
u32 shifted_out_size = cmd_buff[64];
|
||||
u32 my_presence_addr = cmd_buff[65];
|
||||
|
||||
ASSERT(shifted_out_size == ((sizeof(MyPresence) << 14) | 2));
|
||||
|
||||
Memory::WriteBlock(my_presence_addr, reinterpret_cast<const u8*>(&my_presence), sizeof(MyPresence));
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
|
||||
LOG_WARNING(Service_FRD, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void GetFriendKeyList(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
u32 unknown = cmd_buff[1];
|
||||
u32 frd_count = cmd_buff[2];
|
||||
u32 frd_key_addr = cmd_buff[65];
|
||||
|
||||
FriendKey zero_key = {};
|
||||
for (u32 i = 0; i < frd_count; ++i) {
|
||||
Memory::WriteBlock(frd_key_addr + i * sizeof(FriendKey),
|
||||
reinterpret_cast<const u8*>(&zero_key), sizeof(FriendKey));
|
||||
}
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
cmd_buff[2] = 0; // 0 friends
|
||||
LOG_WARNING(Service_FRD, "(STUBBED) called, unknown=%d, frd_count=%d, frd_key_addr=0x%08X",
|
||||
unknown, frd_count, frd_key_addr);
|
||||
}
|
||||
|
||||
void GetFriendProfile(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
u32 count = cmd_buff[1];
|
||||
u32 frd_key_addr = cmd_buff[3];
|
||||
u32 profiles_addr = cmd_buff[65];
|
||||
|
||||
Profile zero_profile = {};
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
Memory::WriteBlock(profiles_addr + i * sizeof(Profile),
|
||||
reinterpret_cast<const u8*>(&zero_profile), sizeof(Profile));
|
||||
}
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
LOG_WARNING(Service_FRD, "(STUBBED) called, count=%d, frd_key_addr=0x%08X, profiles_addr=0x%08X",
|
||||
count, frd_key_addr, profiles_addr);
|
||||
}
|
||||
|
||||
void GetFriendAttributeFlags(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
u32 count = cmd_buff[1];
|
||||
u32 frd_key_addr = cmd_buff[3];
|
||||
u32 attr_flags_addr = cmd_buff[65];
|
||||
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
//TODO:(mailwl) figure out AttributeFlag size and zero all buffer. Assume 1 byte
|
||||
Memory::Write8(attr_flags_addr + i, 0);
|
||||
}
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
LOG_WARNING(Service_FRD, "(STUBBED) called, count=%d, frd_key_addr=0x%08X, attr_flags_addr=0x%08X",
|
||||
count, frd_key_addr, attr_flags_addr);
|
||||
}
|
||||
|
||||
void GetMyFriendKey(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
Memory::WriteBlock(cmd_buff[2], reinterpret_cast<const u8*>(&my_friend_key), sizeof(FriendKey));
|
||||
LOG_WARNING(Service_FRD, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void GetMyScreenName(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
// TODO: (mailwl) get the name from config
|
||||
Common::UTF8ToUTF16("Citra").copy(reinterpret_cast<char16_t*>(&cmd_buff[2]), 11);
|
||||
LOG_WARNING(Service_FRD, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void Init() {
|
||||
using namespace Kernel;
|
||||
|
||||
|
@ -4,9 +4,97 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service {
|
||||
|
||||
class Interface;
|
||||
|
||||
namespace FRD {
|
||||
|
||||
struct FriendKey {
|
||||
u32 friend_id;
|
||||
u32 unknown;
|
||||
u64 friend_code;
|
||||
};
|
||||
|
||||
struct MyPresence {
|
||||
u8 unknown[0x12C];
|
||||
};
|
||||
|
||||
struct Profile {
|
||||
u8 region;
|
||||
u8 country;
|
||||
u8 area;
|
||||
u8 language;
|
||||
u32 unknown;
|
||||
};
|
||||
|
||||
/**
|
||||
* FRD::GetMyPresence service function
|
||||
* Inputs:
|
||||
* 64 : sizeof (MyPresence) << 14 | 2
|
||||
* 65 : Address of MyPresence structure
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
*/
|
||||
void GetMyPresence(Service::Interface* self);
|
||||
|
||||
/**
|
||||
* FRD::GetFriendKeyList service function
|
||||
* Inputs:
|
||||
* 1 : Unknown
|
||||
* 2 : Max friends count
|
||||
* 65 : Address of FriendKey List
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : FriendKey count filled
|
||||
*/
|
||||
void GetFriendKeyList(Service::Interface* self);
|
||||
|
||||
/**
|
||||
* FRD::GetFriendProfile service function
|
||||
* Inputs:
|
||||
* 1 : Friends count
|
||||
* 2 : Friends count << 18 | 2
|
||||
* 3 : Address of FriendKey List
|
||||
* 64 : (count * sizeof (Profile)) << 10 | 2
|
||||
* 65 : Address of Profiles List
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
*/
|
||||
void GetFriendProfile(Service::Interface* self);
|
||||
|
||||
/**
|
||||
* FRD::GetFriendAttributeFlags service function
|
||||
* Inputs:
|
||||
* 1 : Friends count
|
||||
* 2 : Friends count << 18 | 2
|
||||
* 3 : Address of FriendKey List
|
||||
* 65 : Address of AttributeFlags
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
*/
|
||||
void GetFriendAttributeFlags(Service::Interface* self);
|
||||
|
||||
/**
|
||||
* FRD::GetMyFriendKey service function
|
||||
* Inputs:
|
||||
* none
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2-5 : FriendKey
|
||||
*/
|
||||
void GetMyFriendKey(Service::Interface* self);
|
||||
|
||||
/**
|
||||
* FRD::GetMyScreenName service function
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : UTF16 encoded name (max 11 symbols)
|
||||
*/
|
||||
void GetMyScreenName(Service::Interface* self);
|
||||
|
||||
/// Initialize FRD service(s)
|
||||
void Init();
|
||||
|
||||
|
@ -2,65 +2,66 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/frd/frd.h"
|
||||
#include "core/hle/service/frd/frd_u.h"
|
||||
|
||||
namespace Service {
|
||||
namespace FRD {
|
||||
|
||||
const Interface::FunctionInfo FunctionTable[] = {
|
||||
{0x00010000, nullptr, "HasLoggedIn"},
|
||||
{0x00020000, nullptr, "IsOnline"},
|
||||
{0x00030000, nullptr, "Login"},
|
||||
{0x00040000, nullptr, "Logout"},
|
||||
{0x00050000, nullptr, "GetMyFriendKey"},
|
||||
{0x00060000, nullptr, "GetMyPreference"},
|
||||
{0x00070000, nullptr, "GetMyProfile"},
|
||||
{0x00080000, nullptr, "GetMyPresence"},
|
||||
{0x00090000, nullptr, "GetMyScreenName"},
|
||||
{0x000A0000, nullptr, "GetMyMii"},
|
||||
{0x000B0000, nullptr, "GetMyLocalAccountId"},
|
||||
{0x000C0000, nullptr, "GetMyPlayingGame"},
|
||||
{0x000D0000, nullptr, "GetMyFavoriteGame"},
|
||||
{0x000E0000, nullptr, "GetMyNcPrincipalId"},
|
||||
{0x000F0000, nullptr, "GetMyComment"},
|
||||
{0x00100040, nullptr, "GetMyPassword"},
|
||||
{0x00110080, nullptr, "GetFriendKeyList"},
|
||||
{0x00120042, nullptr, "GetFriendPresence"},
|
||||
{0x00130142, nullptr, "GetFriendScreenName"},
|
||||
{0x00140044, nullptr, "GetFriendMii"},
|
||||
{0x00150042, nullptr, "GetFriendProfile"},
|
||||
{0x00160042, nullptr, "GetFriendRelationship"},
|
||||
{0x00170042, nullptr, "GetFriendAttributeFlags"},
|
||||
{0x00180044, nullptr, "GetFriendPlayingGame"},
|
||||
{0x00190042, nullptr, "GetFriendFavoriteGame"},
|
||||
{0x001A00C4, nullptr, "GetFriendInfo"},
|
||||
{0x001B0080, nullptr, "IsIncludedInFriendList"},
|
||||
{0x001C0042, nullptr, "UnscrambleLocalFriendCode"},
|
||||
{0x001D0002, nullptr, "UpdateGameModeDescription"},
|
||||
{0x001E02C2, nullptr, "UpdateGameMode"},
|
||||
{0x001F0042, nullptr, "SendInvitation"},
|
||||
{0x00200002, nullptr, "AttachToEventNotification"},
|
||||
{0x00210040, nullptr, "SetNotificationMask"},
|
||||
{0x00220040, nullptr, "GetEventNotification"},
|
||||
{0x00230000, nullptr, "GetLastResponseResult"},
|
||||
{0x00240040, nullptr, "PrincipalIdToFriendCode"},
|
||||
{0x00250080, nullptr, "FriendCodeToPrincipalId"},
|
||||
{0x00260080, nullptr, "IsValidFriendCode"},
|
||||
{0x00270040, nullptr, "ResultToErrorCode"},
|
||||
{0x00280244, nullptr, "RequestGameAuthentication"},
|
||||
{0x00290000, nullptr, "GetGameAuthenticationData"},
|
||||
{0x002A0204, nullptr, "RequestServiceLocator"},
|
||||
{0x002B0000, nullptr, "GetServiceLocatorData"},
|
||||
{0x002C0002, nullptr, "DetectNatProperties"},
|
||||
{0x002D0000, nullptr, "GetNatProperties"},
|
||||
{0x002E0000, nullptr, "GetServerTimeInterval"},
|
||||
{0x002F0040, nullptr, "AllowHalfAwake"},
|
||||
{0x00300000, nullptr, "GetServerTypes"},
|
||||
{0x00310082, nullptr, "GetFriendComment"},
|
||||
{0x00320042, nullptr, "SetClientSdkVersion"},
|
||||
{0x00330000, nullptr, "GetMyApproachContext"},
|
||||
{0x00340046, nullptr, "AddFriendWithApproach"},
|
||||
{0x00350082, nullptr, "DecryptApproachContext"},
|
||||
{0x00010000, nullptr, "HasLoggedIn"},
|
||||
{0x00020000, nullptr, "IsOnline"},
|
||||
{0x00030000, nullptr, "Login"},
|
||||
{0x00040000, nullptr, "Logout"},
|
||||
{0x00050000, GetMyFriendKey, "GetMyFriendKey"},
|
||||
{0x00060000, nullptr, "GetMyPreference"},
|
||||
{0x00070000, nullptr, "GetMyProfile"},
|
||||
{0x00080000, GetMyPresence, "GetMyPresence"},
|
||||
{0x00090000, GetMyScreenName, "GetMyScreenName"},
|
||||
{0x000A0000, nullptr, "GetMyMii"},
|
||||
{0x000B0000, nullptr, "GetMyLocalAccountId"},
|
||||
{0x000C0000, nullptr, "GetMyPlayingGame"},
|
||||
{0x000D0000, nullptr, "GetMyFavoriteGame"},
|
||||
{0x000E0000, nullptr, "GetMyNcPrincipalId"},
|
||||
{0x000F0000, nullptr, "GetMyComment"},
|
||||
{0x00100040, nullptr, "GetMyPassword"},
|
||||
{0x00110080, GetFriendKeyList, "GetFriendKeyList"},
|
||||
{0x00120042, nullptr, "GetFriendPresence"},
|
||||
{0x00130142, nullptr, "GetFriendScreenName"},
|
||||
{0x00140044, nullptr, "GetFriendMii"},
|
||||
{0x00150042, GetFriendProfile, "GetFriendProfile"},
|
||||
{0x00160042, nullptr, "GetFriendRelationship"},
|
||||
{0x00170042, GetFriendAttributeFlags, "GetFriendAttributeFlags"},
|
||||
{0x00180044, nullptr, "GetFriendPlayingGame"},
|
||||
{0x00190042, nullptr, "GetFriendFavoriteGame"},
|
||||
{0x001A00C4, nullptr, "GetFriendInfo"},
|
||||
{0x001B0080, nullptr, "IsIncludedInFriendList"},
|
||||
{0x001C0042, nullptr, "UnscrambleLocalFriendCode"},
|
||||
{0x001D0002, nullptr, "UpdateGameModeDescription"},
|
||||
{0x001E02C2, nullptr, "UpdateGameMode"},
|
||||
{0x001F0042, nullptr, "SendInvitation"},
|
||||
{0x00200002, nullptr, "AttachToEventNotification"},
|
||||
{0x00210040, nullptr, "SetNotificationMask"},
|
||||
{0x00220040, nullptr, "GetEventNotification"},
|
||||
{0x00230000, nullptr, "GetLastResponseResult"},
|
||||
{0x00240040, nullptr, "PrincipalIdToFriendCode"},
|
||||
{0x00250080, nullptr, "FriendCodeToPrincipalId"},
|
||||
{0x00260080, nullptr, "IsValidFriendCode"},
|
||||
{0x00270040, nullptr, "ResultToErrorCode"},
|
||||
{0x00280244, nullptr, "RequestGameAuthentication"},
|
||||
{0x00290000, nullptr, "GetGameAuthenticationData"},
|
||||
{0x002A0204, nullptr, "RequestServiceLocator"},
|
||||
{0x002B0000, nullptr, "GetServiceLocatorData"},
|
||||
{0x002C0002, nullptr, "DetectNatProperties"},
|
||||
{0x002D0000, nullptr, "GetNatProperties"},
|
||||
{0x002E0000, nullptr, "GetServerTimeInterval"},
|
||||
{0x002F0040, nullptr, "AllowHalfAwake"},
|
||||
{0x00300000, nullptr, "GetServerTypes"},
|
||||
{0x00310082, nullptr, "GetFriendComment"},
|
||||
{0x00320042, nullptr, "SetClientSdkVersion"},
|
||||
{0x00330000, nullptr, "GetMyApproachContext"},
|
||||
{0x00340046, nullptr, "AddFriendWithApproach"},
|
||||
{0x00350082, nullptr, "DecryptApproachContext"},
|
||||
};
|
||||
|
||||
FRD_U_Interface::FRD_U_Interface() {
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/make_unique.h"
|
||||
|
||||
#include "core/file_sys/archive_backend.h"
|
||||
#include "core/file_sys/archive_extsavedata.h"
|
||||
@ -115,6 +114,7 @@ ResultVal<bool> File::SyncRequest() {
|
||||
return read.Code();
|
||||
}
|
||||
cmd_buff[2] = static_cast<u32>(*read);
|
||||
Memory::RasterizerFlushAndInvalidateRegion(Memory::VirtualToPhysicalAddress(address), length);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -521,23 +521,23 @@ void ArchiveInit() {
|
||||
|
||||
std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX);
|
||||
std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
|
||||
auto sdmc_factory = Common::make_unique<FileSys::ArchiveFactory_SDMC>(sdmc_directory);
|
||||
auto sdmc_factory = std::make_unique<FileSys::ArchiveFactory_SDMC>(sdmc_directory);
|
||||
if (sdmc_factory->Initialize())
|
||||
RegisterArchiveType(std::move(sdmc_factory), ArchiveIdCode::SDMC);
|
||||
else
|
||||
LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str());
|
||||
|
||||
// Create the SaveData archive
|
||||
auto savedata_factory = Common::make_unique<FileSys::ArchiveFactory_SaveData>(sdmc_directory);
|
||||
auto savedata_factory = std::make_unique<FileSys::ArchiveFactory_SaveData>(sdmc_directory);
|
||||
RegisterArchiveType(std::move(savedata_factory), ArchiveIdCode::SaveData);
|
||||
|
||||
auto extsavedata_factory = Common::make_unique<FileSys::ArchiveFactory_ExtSaveData>(sdmc_directory, false);
|
||||
auto extsavedata_factory = std::make_unique<FileSys::ArchiveFactory_ExtSaveData>(sdmc_directory, false);
|
||||
if (extsavedata_factory->Initialize())
|
||||
RegisterArchiveType(std::move(extsavedata_factory), ArchiveIdCode::ExtSaveData);
|
||||
else
|
||||
LOG_ERROR(Service_FS, "Can't instantiate ExtSaveData archive with path %s", extsavedata_factory->GetMountPoint().c_str());
|
||||
|
||||
auto sharedextsavedata_factory = Common::make_unique<FileSys::ArchiveFactory_ExtSaveData>(nand_directory, true);
|
||||
auto sharedextsavedata_factory = std::make_unique<FileSys::ArchiveFactory_ExtSaveData>(nand_directory, true);
|
||||
if (sharedextsavedata_factory->Initialize())
|
||||
RegisterArchiveType(std::move(sharedextsavedata_factory), ArchiveIdCode::SharedExtSaveData);
|
||||
else
|
||||
@ -545,10 +545,10 @@ void ArchiveInit() {
|
||||
sharedextsavedata_factory->GetMountPoint().c_str());
|
||||
|
||||
// Create the SaveDataCheck archive, basically a small variation of the RomFS archive
|
||||
auto savedatacheck_factory = Common::make_unique<FileSys::ArchiveFactory_SaveDataCheck>(nand_directory);
|
||||
auto savedatacheck_factory = std::make_unique<FileSys::ArchiveFactory_SaveDataCheck>(nand_directory);
|
||||
RegisterArchiveType(std::move(savedatacheck_factory), ArchiveIdCode::SaveDataCheck);
|
||||
|
||||
auto systemsavedata_factory = Common::make_unique<FileSys::ArchiveFactory_SystemSaveData>(nand_directory);
|
||||
auto systemsavedata_factory = std::make_unique<FileSys::ArchiveFactory_SystemSaveData>(nand_directory);
|
||||
RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData);
|
||||
}
|
||||
|
||||
|
@ -250,7 +250,7 @@ static void CreateFile(Service::Interface* self) {
|
||||
|
||||
FileSys::Path file_path(filename_type, filename_size, filename_ptr);
|
||||
|
||||
LOG_DEBUG(Service_FS, "type=%d size=%llu data=%s", filename_type, filename_size, file_path.DebugStr().c_str());
|
||||
LOG_DEBUG(Service_FS, "type=%d size=%llu data=%s", filename_type, file_size, file_path.DebugStr().c_str());
|
||||
|
||||
cmd_buff[1] = CreateFileInArchive(archive_handle, file_path, file_size).raw;
|
||||
}
|
||||
|
@ -15,8 +15,6 @@
|
||||
|
||||
#include "video_core/gpu_debugger.h"
|
||||
#include "video_core/debug_utils/debug_utils.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
#include "gsp_gpu.h"
|
||||
|
||||
@ -291,8 +289,6 @@ static void FlushDataCache(Service::Interface* self) {
|
||||
u32 size = cmd_buff[2];
|
||||
u32 process = cmd_buff[4];
|
||||
|
||||
VideoCore::g_renderer->Rasterizer()->InvalidateRegion(Memory::VirtualToPhysicalAddress(address), size);
|
||||
|
||||
// TODO(purpasmart96): Verify return header on HW
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
@ -408,6 +404,8 @@ void SignalInterrupt(InterruptId interrupt_id) {
|
||||
g_interrupt_event->Signal();
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(GPU_GSP_DMA, "GPU", "GSP DMA", MP_RGB(100, 0, 255));
|
||||
|
||||
/// Executes the next GSP command
|
||||
static void ExecuteCommand(const Command& command, u32 thread_id) {
|
||||
// Utility function to convert register ID to address
|
||||
@ -419,18 +417,21 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
|
||||
|
||||
// GX request DMA - typically used for copying memory from GSP heap to VRAM
|
||||
case CommandId::REQUEST_DMA:
|
||||
VideoCore::g_renderer->Rasterizer()->FlushRegion(Memory::VirtualToPhysicalAddress(command.dma_request.source_address),
|
||||
command.dma_request.size);
|
||||
{
|
||||
MICROPROFILE_SCOPE(GPU_GSP_DMA);
|
||||
|
||||
// TODO: Consider attempting rasterizer-accelerated surface blit if that usage is ever possible/likely
|
||||
Memory::RasterizerFlushRegion(Memory::VirtualToPhysicalAddress(command.dma_request.source_address),
|
||||
command.dma_request.size);
|
||||
Memory::RasterizerFlushAndInvalidateRegion(Memory::VirtualToPhysicalAddress(command.dma_request.dest_address),
|
||||
command.dma_request.size);
|
||||
|
||||
memcpy(Memory::GetPointer(command.dma_request.dest_address),
|
||||
Memory::GetPointer(command.dma_request.source_address),
|
||||
command.dma_request.size);
|
||||
SignalInterrupt(InterruptId::DMA);
|
||||
|
||||
VideoCore::g_renderer->Rasterizer()->InvalidateRegion(Memory::VirtualToPhysicalAddress(command.dma_request.dest_address),
|
||||
command.dma_request.size);
|
||||
break;
|
||||
|
||||
}
|
||||
// TODO: This will need some rework in the future. (why?)
|
||||
case CommandId::SUBMIT_GPU_CMDLIST:
|
||||
{
|
||||
@ -517,13 +518,8 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
|
||||
|
||||
case CommandId::CACHE_FLUSH:
|
||||
{
|
||||
for (auto& region : command.cache_flush.regions) {
|
||||
if (region.size == 0)
|
||||
break;
|
||||
|
||||
VideoCore::g_renderer->Rasterizer()->InvalidateRegion(
|
||||
Memory::VirtualToPhysicalAddress(region.address), region.size);
|
||||
}
|
||||
// NOTE: Rasterizer flushing handled elsewhere in CPU read/write and other GPU handlers
|
||||
// Use command.cache_flush.regions to implement this handler
|
||||
break;
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user