mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-26 15:40:05 +00:00
Resolved merge conflict in travis-build-docker.sh
This commit is contained in:
commit
ba479ac20e
@ -14,7 +14,7 @@ echo y | sh cmake-3.9.0-Linux-x86_64.sh --prefix=cmake
|
||||
export PATH=/citra/cmake/cmake-3.9.0-Linux-x86_64/bin:$PATH
|
||||
|
||||
mkdir build && cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DENABLE_CUSTOM_SYSTEM_ARCHIVES=1
|
||||
cmake .. -DUSE_SYSTEM_CURL=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_CUSTOM_SYSTEM_ARCHIVES=1
|
||||
make -j4
|
||||
|
||||
ctest -VV -C Release
|
||||
|
BIN
dist/icons/checked.png
vendored
Normal file
BIN
dist/icons/checked.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 451 B |
BIN
dist/icons/failed.png
vendored
Normal file
BIN
dist/icons/failed.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 428 B |
6
dist/icons/icons.qrc
vendored
Normal file
6
dist/icons/icons.qrc
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
<RCC>
|
||||
<qresource prefix="icons">
|
||||
<file>checked.png</file>
|
||||
<file>failed.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
@ -164,6 +164,8 @@ void Config::ReadValues() {
|
||||
sdl2_config->GetBoolean("WebService", "enable_telemetry", true);
|
||||
Settings::values.telemetry_endpoint_url = sdl2_config->Get(
|
||||
"WebService", "telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry");
|
||||
Settings::values.verify_endpoint_url = sdl2_config->Get(
|
||||
"WebService", "verify_endpoint_url", "https://services.citra-emu.org/api/profile");
|
||||
Settings::values.citra_username = sdl2_config->Get("WebService", "citra_username", "");
|
||||
Settings::values.citra_token = sdl2_config->Get("WebService", "citra_token", "");
|
||||
}
|
||||
|
@ -188,6 +188,8 @@ gdbstub_port=24689
|
||||
enable_telemetry =
|
||||
# Endpoint URL for submitting telemetry data
|
||||
telemetry_endpoint_url = https://services.citra-emu.org/api/telemetry
|
||||
# Endpoint URL to verify the username and token
|
||||
verify_endpoint_url = https://services.citra-emu.org/api/profile
|
||||
# Username and token for Citra Web Service
|
||||
# See https://services.citra-emu.org/ for more info
|
||||
citra_username =
|
||||
|
@ -79,6 +79,7 @@ set(UIS
|
||||
main.ui
|
||||
)
|
||||
|
||||
file(GLOB_RECURSE ICONS ${CMAKE_SOURCE_DIR}/dist/icons/*)
|
||||
file(GLOB_RECURSE THEMES ${CMAKE_SOURCE_DIR}/dist/qt_themes/*)
|
||||
|
||||
create_directory_groups(${SRCS} ${HEADERS} ${UIS})
|
||||
@ -92,10 +93,10 @@ endif()
|
||||
if (APPLE)
|
||||
set(MACOSX_ICON "../../dist/citra.icns")
|
||||
set_source_files_properties(${MACOSX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
add_executable(citra-qt MACOSX_BUNDLE ${SRCS} ${HEADERS} ${UI_HDRS} ${THEMES} ${MACOSX_ICON})
|
||||
add_executable(citra-qt MACOSX_BUNDLE ${SRCS} ${HEADERS} ${UI_HDRS} ${ICONS} ${THEMES} ${MACOSX_ICON})
|
||||
set_target_properties(citra-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist)
|
||||
else()
|
||||
add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS} ${THEMES})
|
||||
add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS} ${ICONS} ${THEMES})
|
||||
endif()
|
||||
target_link_libraries(citra-qt PRIVATE audio_core common core input_common network video_core)
|
||||
target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::OpenGL Qt5::Widgets)
|
||||
|
@ -150,6 +150,10 @@ void Config::ReadValues() {
|
||||
qt_config->value("telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry")
|
||||
.toString()
|
||||
.toStdString();
|
||||
Settings::values.verify_endpoint_url =
|
||||
qt_config->value("verify_endpoint_url", "https://services.citra-emu.org/api/profile")
|
||||
.toString()
|
||||
.toStdString();
|
||||
Settings::values.citra_username = qt_config->value("citra_username").toString().toStdString();
|
||||
Settings::values.citra_token = qt_config->value("citra_token").toString().toStdString();
|
||||
qt_config->endGroup();
|
||||
@ -299,6 +303,8 @@ void Config::SaveValues() {
|
||||
qt_config->setValue("enable_telemetry", Settings::values.enable_telemetry);
|
||||
qt_config->setValue("telemetry_endpoint_url",
|
||||
QString::fromStdString(Settings::values.telemetry_endpoint_url));
|
||||
qt_config->setValue("verify_endpoint_url",
|
||||
QString::fromStdString(Settings::values.verify_endpoint_url));
|
||||
qt_config->setValue("citra_username", QString::fromStdString(Settings::values.citra_username));
|
||||
qt_config->setValue("citra_token", QString::fromStdString(Settings::values.citra_token));
|
||||
qt_config->endGroup();
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QMessageBox>
|
||||
#include "citra_qt/configuration/configure_web.h"
|
||||
#include "core/settings.h"
|
||||
#include "core/telemetry_session.h"
|
||||
@ -11,7 +12,9 @@ ConfigureWeb::ConfigureWeb(QWidget* parent)
|
||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureWeb>()) {
|
||||
ui->setupUi(this);
|
||||
connect(ui->button_regenerate_telemetry_id, &QPushButton::clicked, this,
|
||||
&ConfigureWeb::refreshTelemetryID);
|
||||
&ConfigureWeb::RefreshTelemetryID);
|
||||
connect(ui->button_verify_login, &QPushButton::clicked, this, &ConfigureWeb::VerifyLogin);
|
||||
connect(this, &ConfigureWeb::LoginVerified, this, &ConfigureWeb::OnLoginVerified);
|
||||
|
||||
this->setConfiguration();
|
||||
}
|
||||
@ -34,19 +37,66 @@ void ConfigureWeb::setConfiguration() {
|
||||
ui->toggle_telemetry->setChecked(Settings::values.enable_telemetry);
|
||||
ui->edit_username->setText(QString::fromStdString(Settings::values.citra_username));
|
||||
ui->edit_token->setText(QString::fromStdString(Settings::values.citra_token));
|
||||
// Connect after setting the values, to avoid calling OnLoginChanged now
|
||||
connect(ui->edit_token, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged);
|
||||
connect(ui->edit_username, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged);
|
||||
ui->label_telemetry_id->setText("Telemetry ID: 0x" +
|
||||
QString::number(Core::GetTelemetryId(), 16).toUpper());
|
||||
user_verified = true;
|
||||
}
|
||||
|
||||
void ConfigureWeb::applyConfiguration() {
|
||||
Settings::values.enable_telemetry = ui->toggle_telemetry->isChecked();
|
||||
Settings::values.citra_username = ui->edit_username->text().toStdString();
|
||||
Settings::values.citra_token = ui->edit_token->text().toStdString();
|
||||
if (user_verified) {
|
||||
Settings::values.citra_username = ui->edit_username->text().toStdString();
|
||||
Settings::values.citra_token = ui->edit_token->text().toStdString();
|
||||
} else {
|
||||
QMessageBox::warning(this, tr("Username and token not verfied"),
|
||||
tr("Username and token were not verified. The changes to your "
|
||||
"username and/or token have not been saved."));
|
||||
}
|
||||
Settings::Apply();
|
||||
}
|
||||
|
||||
void ConfigureWeb::refreshTelemetryID() {
|
||||
void ConfigureWeb::RefreshTelemetryID() {
|
||||
const u64 new_telemetry_id{Core::RegenerateTelemetryId()};
|
||||
ui->label_telemetry_id->setText("Telemetry ID: 0x" +
|
||||
QString::number(new_telemetry_id, 16).toUpper());
|
||||
}
|
||||
|
||||
void ConfigureWeb::OnLoginChanged() {
|
||||
if (ui->edit_username->text().isEmpty() && ui->edit_token->text().isEmpty()) {
|
||||
user_verified = true;
|
||||
ui->label_username_verified->setPixmap(QPixmap(":/icons/checked.png"));
|
||||
ui->label_token_verified->setPixmap(QPixmap(":/icons/checked.png"));
|
||||
} else {
|
||||
user_verified = false;
|
||||
ui->label_username_verified->setPixmap(QPixmap(":/icons/failed.png"));
|
||||
ui->label_token_verified->setPixmap(QPixmap(":/icons/failed.png"));
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureWeb::VerifyLogin() {
|
||||
verified =
|
||||
Core::VerifyLogin(ui->edit_username->text().toStdString(),
|
||||
ui->edit_token->text().toStdString(), [&]() { emit LoginVerified(); });
|
||||
ui->button_verify_login->setDisabled(true);
|
||||
ui->button_verify_login->setText(tr("Verifying"));
|
||||
}
|
||||
|
||||
void ConfigureWeb::OnLoginVerified() {
|
||||
ui->button_verify_login->setEnabled(true);
|
||||
ui->button_verify_login->setText(tr("Verify"));
|
||||
if (verified.get()) {
|
||||
user_verified = true;
|
||||
ui->label_username_verified->setPixmap(QPixmap(":/icons/checked.png"));
|
||||
ui->label_token_verified->setPixmap(QPixmap(":/icons/checked.png"));
|
||||
} else {
|
||||
ui->label_username_verified->setPixmap(QPixmap(":/icons/failed.png"));
|
||||
ui->label_token_verified->setPixmap(QPixmap(":/icons/failed.png"));
|
||||
QMessageBox::critical(
|
||||
this, tr("Verification failed"),
|
||||
tr("Verification failed. Check that you have entered your username and token "
|
||||
"correctly, and that your internet connection is working."));
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <QWidget>
|
||||
|
||||
@ -21,10 +22,19 @@ public:
|
||||
void applyConfiguration();
|
||||
|
||||
public slots:
|
||||
void refreshTelemetryID();
|
||||
void RefreshTelemetryID();
|
||||
void OnLoginChanged();
|
||||
void VerifyLogin();
|
||||
void OnLoginVerified();
|
||||
|
||||
signals:
|
||||
void LoginVerified();
|
||||
|
||||
private:
|
||||
void setConfiguration();
|
||||
|
||||
bool user_verified = true;
|
||||
std::future<bool> verified;
|
||||
|
||||
std::unique_ptr<Ui::ConfigureWeb> ui;
|
||||
};
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
<width>926</width>
|
||||
<height>561</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -31,14 +31,30 @@
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayoutCitraUsername">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_username">
|
||||
<item row="2" column="3">
|
||||
<widget class="QPushButton" name="button_verify_login">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::RightToLeft</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Username: </string>
|
||||
<string>Verify</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="web_signup_link">
|
||||
<property name="text">
|
||||
<string>Sign up</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="3">
|
||||
<widget class="QLineEdit" name="edit_username">
|
||||
<property name="maxLength">
|
||||
<number>36</number>
|
||||
@ -52,7 +68,22 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<item row="1" column="4">
|
||||
<widget class="QLabel" name="label_token_verified">
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_username">
|
||||
<property name="text">
|
||||
<string>Username: </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QLabel" name="label_username_verified">
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="3">
|
||||
<widget class="QLineEdit" name="edit_token">
|
||||
<property name="maxLength">
|
||||
<number>36</number>
|
||||
@ -62,13 +93,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="web_signup_link">
|
||||
<property name="text">
|
||||
<string>Sign up</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="web_token_info_link">
|
||||
<property name="text">
|
||||
@ -76,6 +100,19 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<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>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
@ -105,17 +142,17 @@
|
||||
<layout class="QGridLayout" name="gridLayoutTelemetryId">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_telemetry_id">
|
||||
<property name="text">
|
||||
<string>Telemetry ID:</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Telemetry ID:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="button_regenerate_telemetry_id">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
|
@ -135,7 +135,8 @@ set(SRCS
|
||||
hle/service/nim/nim_aoc.cpp
|
||||
hle/service/nim/nim_s.cpp
|
||||
hle/service/nim/nim_u.cpp
|
||||
hle/service/ns_s.cpp
|
||||
hle/service/ns/ns.cpp
|
||||
hle/service/ns/ns_s.cpp
|
||||
hle/service/nwm/nwm.cpp
|
||||
hle/service/nwm/nwm_cec.cpp
|
||||
hle/service/nwm/nwm_ext.cpp
|
||||
@ -335,7 +336,8 @@ set(HEADERS
|
||||
hle/service/nim/nim_aoc.h
|
||||
hle/service/nim/nim_s.h
|
||||
hle/service/nim/nim_u.h
|
||||
hle/service/ns_s.h
|
||||
hle/service/ns/ns.h
|
||||
hle/service/ns/ns_s.h
|
||||
hle/service/nwm/nwm.h
|
||||
hle/service/nwm/nwm_cec.h
|
||||
hle/service/nwm/nwm_ext.h
|
||||
|
@ -56,7 +56,9 @@ static Dynarmic::UserCallbacks GetUserCallbacks(
|
||||
user_callbacks.memory.Write16 = &Memory::Write16;
|
||||
user_callbacks.memory.Write32 = &Memory::Write32;
|
||||
user_callbacks.memory.Write64 = &Memory::Write64;
|
||||
user_callbacks.page_table = Memory::GetCurrentPageTablePointers();
|
||||
// TODO(Subv): Re-add the page table pointers once dynarmic supports switching page tables at
|
||||
// runtime.
|
||||
user_callbacks.page_table = nullptr;
|
||||
user_callbacks.coprocessors[15] = std::make_shared<DynarmicCP15>(interpeter_state);
|
||||
return user_callbacks;
|
||||
}
|
||||
|
@ -146,7 +146,6 @@ void System::Reschedule() {
|
||||
}
|
||||
|
||||
System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
|
||||
Memory::InitMemoryMap();
|
||||
LOG_DEBUG(HW_Memory, "initialized OK");
|
||||
|
||||
if (Settings::values.use_cpu_jit) {
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "audio_core/audio_core.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
@ -24,7 +23,7 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
static MemoryRegionInfo memory_regions[3];
|
||||
MemoryRegionInfo memory_regions[3];
|
||||
|
||||
/// Size of the APPLICATION, SYSTEM and BASE memory regions (respectively) for each system
|
||||
/// memory configuration type.
|
||||
@ -96,9 +95,6 @@ MemoryRegionInfo* GetMemoryRegion(MemoryRegion region) {
|
||||
}
|
||||
}
|
||||
|
||||
std::array<u8, Memory::VRAM_SIZE> vram;
|
||||
std::array<u8, Memory::N3DS_EXTRA_RAM_SIZE> n3ds_extra_ram;
|
||||
|
||||
void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping) {
|
||||
using namespace Memory;
|
||||
|
||||
@ -143,30 +139,14 @@ void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mappin
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(yuriks): Use GetPhysicalPointer when that becomes independent of the virtual
|
||||
// mappings.
|
||||
u8* target_pointer = nullptr;
|
||||
switch (area->paddr_base) {
|
||||
case VRAM_PADDR:
|
||||
target_pointer = vram.data();
|
||||
break;
|
||||
case DSP_RAM_PADDR:
|
||||
target_pointer = AudioCore::GetDspMemory().data();
|
||||
break;
|
||||
case N3DS_EXTRA_RAM_PADDR:
|
||||
target_pointer = n3ds_extra_ram.data();
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
u8* target_pointer = Memory::GetPhysicalPointer(area->paddr_base + offset_into_region);
|
||||
|
||||
// TODO(yuriks): This flag seems to have some other effect, but it's unknown what
|
||||
MemoryState memory_state = mapping.unk_flag ? MemoryState::Static : MemoryState::IO;
|
||||
|
||||
auto vma = address_space
|
||||
.MapBackingMemory(mapping.address, target_pointer + offset_into_region,
|
||||
mapping.size, memory_state)
|
||||
.Unwrap();
|
||||
auto vma =
|
||||
address_space.MapBackingMemory(mapping.address, target_pointer, mapping.size, memory_state)
|
||||
.Unwrap();
|
||||
address_space.Reprotect(vma,
|
||||
mapping.read_only ? VMAPermission::Read : VMAPermission::ReadWrite);
|
||||
}
|
||||
|
@ -26,4 +26,6 @@ MemoryRegionInfo* GetMemoryRegion(MemoryRegion region);
|
||||
|
||||
void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping);
|
||||
void MapSharedPages(VMManager& address_space);
|
||||
|
||||
extern MemoryRegionInfo memory_regions[3];
|
||||
} // namespace Kernel
|
||||
|
@ -171,6 +171,8 @@ static void SwitchContext(Thread* new_thread) {
|
||||
// Cancel any outstanding wakeup events for this thread
|
||||
CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle);
|
||||
|
||||
auto previous_process = Kernel::g_current_process;
|
||||
|
||||
current_thread = new_thread;
|
||||
|
||||
ready_queue.remove(new_thread->current_priority, new_thread);
|
||||
@ -178,8 +180,18 @@ static void SwitchContext(Thread* new_thread) {
|
||||
|
||||
Core::CPU().LoadContext(new_thread->context);
|
||||
Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress());
|
||||
|
||||
if (previous_process != current_thread->owner_process) {
|
||||
Kernel::g_current_process = current_thread->owner_process;
|
||||
Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table;
|
||||
// We have switched processes and thus, page tables, clear the instruction cache so we
|
||||
// don't keep stale data from the previous process.
|
||||
Core::CPU().ClearInstructionCache();
|
||||
}
|
||||
} else {
|
||||
current_thread = nullptr;
|
||||
// Note: We do not reset the current process and current page table when idling because
|
||||
// technically we haven't changed processes, our threads are just paused.
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,6 +56,10 @@ void VMManager::Reset() {
|
||||
initial_vma.size = MAX_ADDRESS;
|
||||
vma_map.emplace(initial_vma.base, initial_vma);
|
||||
|
||||
page_table.pointers.fill(nullptr);
|
||||
page_table.attributes.fill(Memory::PageType::Unmapped);
|
||||
page_table.cached_res_count.fill(0);
|
||||
|
||||
UpdatePageTableForVMA(initial_vma);
|
||||
}
|
||||
|
||||
@ -328,16 +332,17 @@ VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) {
|
||||
void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
|
||||
switch (vma.type) {
|
||||
case VMAType::Free:
|
||||
Memory::UnmapRegion(vma.base, vma.size);
|
||||
Memory::UnmapRegion(page_table, vma.base, vma.size);
|
||||
break;
|
||||
case VMAType::AllocatedMemoryBlock:
|
||||
Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_block->data() + vma.offset);
|
||||
Memory::MapMemoryRegion(page_table, vma.base, vma.size,
|
||||
vma.backing_block->data() + vma.offset);
|
||||
break;
|
||||
case VMAType::BackingMemory:
|
||||
Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_memory);
|
||||
Memory::MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory);
|
||||
break;
|
||||
case VMAType::MMIO:
|
||||
Memory::MapIoRegion(vma.base, vma.size, vma.mmio_handler);
|
||||
Memory::MapIoRegion(page_table, vma.base, vma.size, vma.mmio_handler);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/mmio.h"
|
||||
|
||||
namespace Kernel {
|
||||
@ -102,7 +103,6 @@ struct VirtualMemoryArea {
|
||||
* - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/
|
||||
*/
|
||||
class VMManager final {
|
||||
// TODO(yuriks): Make page tables switchable to support multiple VMManagers
|
||||
public:
|
||||
/**
|
||||
* The maximum amount of address space managed by the kernel. Addresses above this are never
|
||||
@ -184,6 +184,10 @@ public:
|
||||
/// Dumps the address space layout to the log, for debugging
|
||||
void LogLayout(Log::Level log_level) const;
|
||||
|
||||
/// Each VMManager has its own page table, which is set as the main one when the owning process
|
||||
/// is scheduled.
|
||||
Memory::PageTable page_table;
|
||||
|
||||
private:
|
||||
using VMAIter = decltype(vma_map)::iterator;
|
||||
|
||||
|
16
src/core/hle/service/ns/ns.cpp
Normal file
16
src/core/hle/service/ns/ns.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/ns/ns.h"
|
||||
#include "core/hle/service/ns/ns_s.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NS {
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
std::make_shared<NS_S>()->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
} // namespace NS
|
||||
} // namespace Service
|
16
src/core/hle/service/ns/ns.h
Normal file
16
src/core/hle/service/ns/ns.h
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NS {
|
||||
|
||||
/// Registers all NS services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
|
||||
} // namespace NS
|
||||
} // namespace Service
|
34
src/core/hle/service/ns/ns_s.cpp
Normal file
34
src/core/hle/service/ns/ns_s.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/ns/ns_s.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NS {
|
||||
|
||||
NS_S::NS_S() : ServiceFramework("ns:s", 2) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0x000100C0, nullptr, "LaunchFIRM"},
|
||||
{0x000200C0, nullptr, "LaunchTitle"},
|
||||
{0x00030000, nullptr, "TerminateApplication"},
|
||||
{0x00040040, nullptr, "TerminateProcess"},
|
||||
{0x000500C0, nullptr, "LaunchApplicationFIRM"},
|
||||
{0x00060042, nullptr, "SetFIRMParams4A0"},
|
||||
{0x00070042, nullptr, "CardUpdateInitialize"},
|
||||
{0x00080000, nullptr, "CardUpdateShutdown"},
|
||||
{0x000D0140, nullptr, "SetTWLBannerHMAC"},
|
||||
{0x000E0000, nullptr, "ShutdownAsync"},
|
||||
{0x00100180, nullptr, "RebootSystem"},
|
||||
{0x00110100, nullptr, "TerminateTitle"},
|
||||
{0x001200C0, nullptr, "SetApplicationCpuTimeLimit"},
|
||||
{0x00150140, nullptr, "LaunchApplication"},
|
||||
{0x00160000, nullptr, "RebootSystemClean"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
NS_S::~NS_S() = default;
|
||||
|
||||
} // namespace NS
|
||||
} // namespace Service
|
@ -4,18 +4,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NS {
|
||||
|
||||
class NS_S final : public Interface {
|
||||
/// Interface to "ns:s" service
|
||||
class NS_S final : public ServiceFramework<NS_S> {
|
||||
public:
|
||||
NS_S();
|
||||
|
||||
std::string GetPortName() const override {
|
||||
return "ns:s";
|
||||
}
|
||||
~NS_S();
|
||||
};
|
||||
|
||||
} // namespace NS
|
@ -1,33 +0,0 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/ns_s.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NS {
|
||||
|
||||
const Interface::FunctionInfo FunctionTable[] = {
|
||||
{0x000100C0, nullptr, "LaunchFIRM"},
|
||||
{0x000200C0, nullptr, "LaunchTitle"},
|
||||
{0x00030000, nullptr, "TerminateApplication"},
|
||||
{0x00040040, nullptr, "TerminateProcess"},
|
||||
{0x000500C0, nullptr, "LaunchApplicationFIRM"},
|
||||
{0x00060042, nullptr, "SetFIRMParams4A0"},
|
||||
{0x00070042, nullptr, "CardUpdateInitialize"},
|
||||
{0x00080000, nullptr, "CardUpdateShutdown"},
|
||||
{0x000D0140, nullptr, "SetTWLBannerHMAC"},
|
||||
{0x000E0000, nullptr, "ShutdownAsync"},
|
||||
{0x00100180, nullptr, "RebootSystem"},
|
||||
{0x00110100, nullptr, "TerminateTitle"},
|
||||
{0x001200C0, nullptr, "SetApplicationCpuTimeLimit"},
|
||||
{0x00150140, nullptr, "LaunchApplication"},
|
||||
{0x00160000, nullptr, "RebootSystemClean"},
|
||||
};
|
||||
|
||||
NS_S::NS_S() {
|
||||
Register(FunctionTable);
|
||||
}
|
||||
|
||||
} // namespace NS
|
||||
} // namespace Service
|
@ -38,7 +38,7 @@
|
||||
#include "core/hle/service/news/news.h"
|
||||
#include "core/hle/service/nfc/nfc.h"
|
||||
#include "core/hle/service/nim/nim.h"
|
||||
#include "core/hle/service/ns_s.h"
|
||||
#include "core/hle/service/ns/ns.h"
|
||||
#include "core/hle/service/nwm/nwm.h"
|
||||
#include "core/hle/service/pm_app.h"
|
||||
#include "core/hle/service/ptm/ptm.h"
|
||||
@ -215,6 +215,8 @@ void Init() {
|
||||
SM::g_service_manager = std::make_shared<SM::ServiceManager>();
|
||||
SM::ServiceManager::InstallInterfaces(SM::g_service_manager);
|
||||
|
||||
NS::InstallInterfaces(*SM::g_service_manager);
|
||||
|
||||
AddNamedPort(new ERR::ERR_F);
|
||||
|
||||
FS::ArchiveInit();
|
||||
@ -246,7 +248,6 @@ void Init() {
|
||||
AddService(new HTTP::HTTP_C);
|
||||
AddService(new LDR::LDR_RO);
|
||||
AddService(new MIC::MIC_U);
|
||||
AddService(new NS::NS_S);
|
||||
AddService(new PM::PM_APP);
|
||||
AddService(new SOC::SOC_U);
|
||||
AddService(new SSL::SSL_C);
|
||||
|
@ -270,6 +270,7 @@ ResultStatus AppLoader_THREEDSX::Load() {
|
||||
Kernel::g_current_process = Kernel::Process::Create(std::move(codeset));
|
||||
Kernel::g_current_process->svc_access_mask.set();
|
||||
Kernel::g_current_process->address_mappings = default_address_mappings;
|
||||
Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table;
|
||||
|
||||
// Attach the default resource limit (APPLICATION) to the process
|
||||
Kernel::g_current_process->resource_limit =
|
||||
|
@ -397,6 +397,7 @@ ResultStatus AppLoader_ELF::Load() {
|
||||
Kernel::g_current_process = Kernel::Process::Create(std::move(codeset));
|
||||
Kernel::g_current_process->svc_access_mask.set();
|
||||
Kernel::g_current_process->address_mappings = default_address_mappings;
|
||||
Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table;
|
||||
|
||||
// Attach the default resource limit (APPLICATION) to the process
|
||||
Kernel::g_current_process->resource_limit =
|
||||
|
@ -172,6 +172,7 @@ ResultStatus AppLoader_NCCH::LoadExec() {
|
||||
codeset->memory = std::make_shared<std::vector<u8>>(std::move(code));
|
||||
|
||||
Kernel::g_current_process = Kernel::Process::Create(std::move(codeset));
|
||||
Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table;
|
||||
|
||||
// Attach a resource limit to the process based on the resource limit category
|
||||
Kernel::g_current_process->resource_limit =
|
||||
|
@ -4,83 +4,31 @@
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include "audio_core/audio_core.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/kernel/memory.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/memory_setup.h"
|
||||
#include "core/mmio.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace Memory {
|
||||
|
||||
enum class PageType {
|
||||
/// Page is unmapped and should cause an access error.
|
||||
Unmapped,
|
||||
/// Page is mapped to regular memory. This is the only type you can get pointers to.
|
||||
Memory,
|
||||
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
||||
/// invalidation
|
||||
RasterizerCachedMemory,
|
||||
/// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
|
||||
Special,
|
||||
/// Page is mapped to a I/O region, but also needs to check for rasterizer cache flushing and
|
||||
/// invalidation
|
||||
RasterizerCachedSpecial,
|
||||
};
|
||||
static std::array<u8, Memory::VRAM_SIZE> vram;
|
||||
static std::array<u8, Memory::N3DS_EXTRA_RAM_SIZE> n3ds_extra_ram;
|
||||
|
||||
struct SpecialRegion {
|
||||
VAddr base;
|
||||
u32 size;
|
||||
MMIORegionPointer handler;
|
||||
};
|
||||
|
||||
/**
|
||||
* A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
|
||||
* mimics the way a real CPU page table works, but instead is optimized for minimal decoding and
|
||||
* fetching requirements when accessing. In the usual case of an access to regular memory, it only
|
||||
* requires an indexed fetch and a check for NULL.
|
||||
*/
|
||||
struct PageTable {
|
||||
/**
|
||||
* Array of memory pointers backing each page. An entry can only be non-null if the
|
||||
* corresponding entry in the `attributes` array is of type `Memory`.
|
||||
*/
|
||||
std::array<u8*, PAGE_TABLE_NUM_ENTRIES> pointers;
|
||||
|
||||
/**
|
||||
* Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of
|
||||
* type `Special`.
|
||||
*/
|
||||
std::vector<SpecialRegion> special_regions;
|
||||
|
||||
/**
|
||||
* Array of fine grained page attributes. If it is set to any value other than `Memory`, then
|
||||
* the corresponding entry in `pointers` MUST be set to null.
|
||||
*/
|
||||
std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes;
|
||||
|
||||
/**
|
||||
* Indicates the number of externally cached resources touching a page that should be
|
||||
* flushed before the memory is accessed
|
||||
*/
|
||||
std::array<u8, PAGE_TABLE_NUM_ENTRIES> cached_res_count;
|
||||
};
|
||||
|
||||
/// Singular page table used for the singleton process
|
||||
static PageTable main_page_table;
|
||||
/// Currently active page table
|
||||
static PageTable* current_page_table = &main_page_table;
|
||||
PageTable* current_page_table = nullptr;
|
||||
|
||||
std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers() {
|
||||
return ¤t_page_table->pointers;
|
||||
}
|
||||
|
||||
static void MapPages(u32 base, u32 size, u8* memory, PageType type) {
|
||||
static void MapPages(PageTable& page_table, u32 base, u32 size, u8* memory, PageType type) {
|
||||
LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE,
|
||||
(base + size) * PAGE_SIZE);
|
||||
|
||||
@ -91,9 +39,9 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) {
|
||||
while (base != end) {
|
||||
ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %08X", base);
|
||||
|
||||
current_page_table->attributes[base] = type;
|
||||
current_page_table->pointers[base] = memory;
|
||||
current_page_table->cached_res_count[base] = 0;
|
||||
page_table.attributes[base] = type;
|
||||
page_table.pointers[base] = memory;
|
||||
page_table.cached_res_count[base] = 0;
|
||||
|
||||
base += 1;
|
||||
if (memory != nullptr)
|
||||
@ -101,30 +49,24 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) {
|
||||
}
|
||||
}
|
||||
|
||||
void InitMemoryMap() {
|
||||
main_page_table.pointers.fill(nullptr);
|
||||
main_page_table.attributes.fill(PageType::Unmapped);
|
||||
main_page_table.cached_res_count.fill(0);
|
||||
}
|
||||
|
||||
void MapMemoryRegion(VAddr base, u32 size, u8* target) {
|
||||
void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, u8* target) {
|
||||
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size);
|
||||
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base);
|
||||
MapPages(base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory);
|
||||
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory);
|
||||
}
|
||||
|
||||
void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler) {
|
||||
void MapIoRegion(PageTable& page_table, VAddr base, u32 size, MMIORegionPointer mmio_handler) {
|
||||
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size);
|
||||
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base);
|
||||
MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special);
|
||||
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special);
|
||||
|
||||
current_page_table->special_regions.emplace_back(SpecialRegion{base, size, mmio_handler});
|
||||
page_table.special_regions.emplace_back(SpecialRegion{base, size, mmio_handler});
|
||||
}
|
||||
|
||||
void UnmapRegion(VAddr base, u32 size) {
|
||||
void UnmapRegion(PageTable& page_table, VAddr base, u32 size) {
|
||||
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size);
|
||||
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base);
|
||||
MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped);
|
||||
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -273,8 +215,7 @@ bool IsValidVirtualAddress(const VAddr vaddr) {
|
||||
}
|
||||
|
||||
bool IsValidPhysicalAddress(const PAddr paddr) {
|
||||
boost::optional<VAddr> vaddr = PhysicalToVirtualAddress(paddr);
|
||||
return vaddr && IsValidVirtualAddress(*vaddr);
|
||||
return GetPhysicalPointer(paddr) != nullptr;
|
||||
}
|
||||
|
||||
u8* GetPointer(const VAddr vaddr) {
|
||||
@ -306,9 +247,63 @@ std::string ReadCString(VAddr vaddr, std::size_t max_length) {
|
||||
}
|
||||
|
||||
u8* GetPhysicalPointer(PAddr address) {
|
||||
// TODO(Subv): This call should not go through the application's memory mapping.
|
||||
boost::optional<VAddr> vaddr = PhysicalToVirtualAddress(address);
|
||||
return vaddr ? GetPointer(*vaddr) : nullptr;
|
||||
struct MemoryArea {
|
||||
PAddr paddr_base;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
static constexpr MemoryArea memory_areas[] = {
|
||||
{VRAM_PADDR, VRAM_SIZE},
|
||||
{IO_AREA_PADDR, IO_AREA_SIZE},
|
||||
{DSP_RAM_PADDR, DSP_RAM_SIZE},
|
||||
{FCRAM_PADDR, FCRAM_N3DS_SIZE},
|
||||
{N3DS_EXTRA_RAM_PADDR, N3DS_EXTRA_RAM_SIZE},
|
||||
};
|
||||
|
||||
const auto area =
|
||||
std::find_if(std::begin(memory_areas), std::end(memory_areas), [&](const auto& area) {
|
||||
return address >= area.paddr_base && address < area.paddr_base + area.size;
|
||||
});
|
||||
|
||||
if (area == std::end(memory_areas)) {
|
||||
LOG_ERROR(HW_Memory, "unknown GetPhysicalPointer @ 0x%08X", address);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (area->paddr_base == IO_AREA_PADDR) {
|
||||
LOG_ERROR(HW_Memory, "MMIO mappings are not supported yet. phys_addr=0x%08X", address);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u32 offset_into_region = address - area->paddr_base;
|
||||
|
||||
u8* target_pointer = nullptr;
|
||||
switch (area->paddr_base) {
|
||||
case VRAM_PADDR:
|
||||
target_pointer = vram.data() + offset_into_region;
|
||||
break;
|
||||
case DSP_RAM_PADDR:
|
||||
target_pointer = AudioCore::GetDspMemory().data() + offset_into_region;
|
||||
break;
|
||||
case FCRAM_PADDR:
|
||||
for (const auto& region : Kernel::memory_regions) {
|
||||
if (offset_into_region >= region.base &&
|
||||
offset_into_region < region.base + region.size) {
|
||||
target_pointer =
|
||||
region.linear_heap_memory->data() + offset_into_region - region.base;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT_MSG(target_pointer != nullptr, "Invalid FCRAM address");
|
||||
break;
|
||||
case N3DS_EXTRA_RAM_PADDR:
|
||||
target_pointer = n3ds_extra_ram.data() + offset_into_region;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return target_pointer;
|
||||
}
|
||||
|
||||
void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta) {
|
||||
|
@ -7,8 +7,10 @@
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/optional.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/mmio.h"
|
||||
|
||||
namespace Memory {
|
||||
|
||||
@ -21,6 +23,59 @@ const u32 PAGE_MASK = PAGE_SIZE - 1;
|
||||
const int PAGE_BITS = 12;
|
||||
const size_t PAGE_TABLE_NUM_ENTRIES = 1 << (32 - PAGE_BITS);
|
||||
|
||||
enum class PageType {
|
||||
/// Page is unmapped and should cause an access error.
|
||||
Unmapped,
|
||||
/// Page is mapped to regular memory. This is the only type you can get pointers to.
|
||||
Memory,
|
||||
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
||||
/// invalidation
|
||||
RasterizerCachedMemory,
|
||||
/// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
|
||||
Special,
|
||||
/// Page is mapped to a I/O region, but also needs to check for rasterizer cache flushing and
|
||||
/// invalidation
|
||||
RasterizerCachedSpecial,
|
||||
};
|
||||
|
||||
struct SpecialRegion {
|
||||
VAddr base;
|
||||
u32 size;
|
||||
MMIORegionPointer handler;
|
||||
};
|
||||
|
||||
/**
|
||||
* A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
|
||||
* mimics the way a real CPU page table works, but instead is optimized for minimal decoding and
|
||||
* fetching requirements when accessing. In the usual case of an access to regular memory, it only
|
||||
* requires an indexed fetch and a check for NULL.
|
||||
*/
|
||||
struct PageTable {
|
||||
/**
|
||||
* Array of memory pointers backing each page. An entry can only be non-null if the
|
||||
* corresponding entry in the `attributes` array is of type `Memory`.
|
||||
*/
|
||||
std::array<u8*, PAGE_TABLE_NUM_ENTRIES> pointers;
|
||||
|
||||
/**
|
||||
* Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of
|
||||
* type `Special`.
|
||||
*/
|
||||
std::vector<SpecialRegion> special_regions;
|
||||
|
||||
/**
|
||||
* Array of fine grained page attributes. If it is set to any value other than `Memory`, then
|
||||
* the corresponding entry in `pointers` MUST be set to null.
|
||||
*/
|
||||
std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes;
|
||||
|
||||
/**
|
||||
* Indicates the number of externally cached resources touching a page that should be
|
||||
* flushed before the memory is accessed
|
||||
*/
|
||||
std::array<u8, PAGE_TABLE_NUM_ENTRIES> cached_res_count;
|
||||
};
|
||||
|
||||
/// Physical memory regions as seen from the ARM11
|
||||
enum : PAddr {
|
||||
/// IO register area
|
||||
@ -126,6 +181,9 @@ enum : VAddr {
|
||||
NEW_LINEAR_HEAP_VADDR_END = NEW_LINEAR_HEAP_VADDR + NEW_LINEAR_HEAP_SIZE,
|
||||
};
|
||||
|
||||
/// Currently active page table
|
||||
extern PageTable* current_page_table;
|
||||
|
||||
bool IsValidVirtualAddress(const VAddr addr);
|
||||
bool IsValidPhysicalAddress(const PAddr addr);
|
||||
|
||||
@ -169,8 +227,6 @@ boost::optional<VAddr> PhysicalToVirtualAddress(PAddr addr);
|
||||
|
||||
/**
|
||||
* Gets a pointer to the memory region beginning at the specified physical address.
|
||||
*
|
||||
* @note This is currently implemented using PhysicalToVirtualAddress().
|
||||
*/
|
||||
u8* GetPhysicalPointer(PAddr address);
|
||||
|
||||
@ -209,4 +265,4 @@ void RasterizerFlushVirtualRegion(VAddr start, u32 size, FlushMode mode);
|
||||
* retrieve the current page table for that purpose.
|
||||
*/
|
||||
std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers();
|
||||
}
|
||||
} // namespace Memory
|
||||
|
@ -9,24 +9,24 @@
|
||||
|
||||
namespace Memory {
|
||||
|
||||
void InitMemoryMap();
|
||||
|
||||
/**
|
||||
* Maps an allocated buffer onto a region of the emulated process address space.
|
||||
*
|
||||
* @param page_table The page table of the emulated process.
|
||||
* @param base The address to start mapping at. Must be page-aligned.
|
||||
* @param size The amount of bytes to map. Must be page-aligned.
|
||||
* @param target Buffer with the memory backing the mapping. Must be of length at least `size`.
|
||||
*/
|
||||
void MapMemoryRegion(VAddr base, u32 size, u8* target);
|
||||
void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, u8* target);
|
||||
|
||||
/**
|
||||
* Maps a region of the emulated process address space as a IO region.
|
||||
* @param page_table The page table of the emulated process.
|
||||
* @param base The address to start mapping at. Must be page-aligned.
|
||||
* @param size The amount of bytes to map. Must be page-aligned.
|
||||
* @param mmio_handler The handler that backs the mapping.
|
||||
*/
|
||||
void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler);
|
||||
void MapIoRegion(PageTable& page_table, VAddr base, u32 size, MMIORegionPointer mmio_handler);
|
||||
|
||||
void UnmapRegion(VAddr base, u32 size);
|
||||
void UnmapRegion(PageTable& page_table, VAddr base, u32 size);
|
||||
}
|
||||
|
@ -134,6 +134,7 @@ struct Values {
|
||||
// WebService
|
||||
bool enable_telemetry;
|
||||
std::string telemetry_endpoint_url;
|
||||
std::string verify_endpoint_url;
|
||||
std::string citra_username;
|
||||
std::string citra_token;
|
||||
} extern values;
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#ifdef ENABLE_WEB_SERVICE
|
||||
#include "web_service/telemetry_json.h"
|
||||
#include "web_service/verify_login.h"
|
||||
#endif
|
||||
|
||||
namespace Core {
|
||||
@ -75,6 +76,17 @@ u64 RegenerateTelemetryId() {
|
||||
return new_telemetry_id;
|
||||
}
|
||||
|
||||
std::future<bool> VerifyLogin(std::string username, std::string token, std::function<void()> func) {
|
||||
#ifdef ENABLE_WEB_SERVICE
|
||||
return WebService::VerifyLogin(username, token, Settings::values.verify_endpoint_url, func);
|
||||
#else
|
||||
return std::async(std::launch::async, [func{std::move(func)}]() {
|
||||
func();
|
||||
return false;
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
TelemetrySession::TelemetrySession() {
|
||||
#ifdef ENABLE_WEB_SERVICE
|
||||
if (Settings::values.enable_telemetry) {
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include "common/telemetry.h"
|
||||
|
||||
@ -47,4 +48,13 @@ u64 GetTelemetryId();
|
||||
*/
|
||||
u64 RegenerateTelemetryId();
|
||||
|
||||
/**
|
||||
* Verifies the username and token.
|
||||
* @param username Citra username to use for authentication.
|
||||
* @param token Citra token to use for authentication.
|
||||
* @param func A function that gets exectued when the verification is finished
|
||||
* @returns Future with bool indicating whether the verification succeeded
|
||||
*/
|
||||
std::future<bool> VerifyLogin(std::string username, std::string token, std::function<void()> func);
|
||||
|
||||
} // namespace Core
|
||||
|
@ -3,20 +3,30 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/memory_setup.h"
|
||||
#include "tests/core/arm/arm_test_common.h"
|
||||
|
||||
namespace ArmTests {
|
||||
|
||||
static Memory::PageTable page_table;
|
||||
|
||||
TestEnvironment::TestEnvironment(bool mutable_memory_)
|
||||
: mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) {
|
||||
Memory::MapIoRegion(0x00000000, 0x80000000, test_memory);
|
||||
Memory::MapIoRegion(0x80000000, 0x80000000, test_memory);
|
||||
|
||||
page_table.pointers.fill(nullptr);
|
||||
page_table.attributes.fill(Memory::PageType::Unmapped);
|
||||
page_table.cached_res_count.fill(0);
|
||||
|
||||
Memory::MapIoRegion(page_table, 0x00000000, 0x80000000, test_memory);
|
||||
Memory::MapIoRegion(page_table, 0x80000000, 0x80000000, test_memory);
|
||||
|
||||
Memory::current_page_table = &page_table;
|
||||
}
|
||||
|
||||
TestEnvironment::~TestEnvironment() {
|
||||
Memory::UnmapRegion(0x80000000, 0x80000000);
|
||||
Memory::UnmapRegion(0x00000000, 0x80000000);
|
||||
Memory::UnmapRegion(page_table, 0x80000000, 0x80000000);
|
||||
Memory::UnmapRegion(page_table, 0x00000000, 0x80000000);
|
||||
}
|
||||
|
||||
void TestEnvironment::SetMemory64(VAddr vaddr, u64 value) {
|
||||
|
@ -5,10 +5,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/pica_types.h"
|
||||
|
||||
namespace Pica {
|
||||
|
||||
@ -31,7 +31,17 @@ struct RasterizerRegs {
|
||||
|
||||
BitField<0, 24, u32> viewport_size_y;
|
||||
|
||||
INSERT_PADDING_WORDS(0x9);
|
||||
INSERT_PADDING_WORDS(0x3);
|
||||
|
||||
BitField<0, 1, u32> clip_enable;
|
||||
BitField<0, 24, u32> clip_coef[4]; // float24
|
||||
|
||||
Math::Vec4<float24> GetClipCoef() const {
|
||||
return {float24::FromRaw(clip_coef[0]), float24::FromRaw(clip_coef[1]),
|
||||
float24::FromRaw(clip_coef[2]), float24::FromRaw(clip_coef[3])};
|
||||
}
|
||||
|
||||
INSERT_PADDING_WORDS(0x1);
|
||||
|
||||
BitField<0, 24, u32> viewport_depth_range; // float24
|
||||
BitField<0, 24, u32> viewport_depth_near_plane; // float24
|
||||
|
@ -169,6 +169,8 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {
|
||||
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, proctex_diff_lut_buffer.handle);
|
||||
|
||||
// Sync fixed function OpenGL state
|
||||
SyncClipEnabled();
|
||||
SyncClipCoef();
|
||||
SyncCullMode();
|
||||
SyncBlendEnabled();
|
||||
SyncBlendFuncs();
|
||||
@ -401,6 +403,18 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
|
||||
SyncCullMode();
|
||||
break;
|
||||
|
||||
// Clipping plane
|
||||
case PICA_REG_INDEX(rasterizer.clip_enable):
|
||||
SyncClipEnabled();
|
||||
break;
|
||||
|
||||
case PICA_REG_INDEX_WORKAROUND(rasterizer.clip_coef[0], 0x48):
|
||||
case PICA_REG_INDEX_WORKAROUND(rasterizer.clip_coef[1], 0x49):
|
||||
case PICA_REG_INDEX_WORKAROUND(rasterizer.clip_coef[2], 0x4a):
|
||||
case PICA_REG_INDEX_WORKAROUND(rasterizer.clip_coef[3], 0x4b):
|
||||
SyncClipCoef();
|
||||
break;
|
||||
|
||||
// Depth modifiers
|
||||
case PICA_REG_INDEX(rasterizer.viewport_depth_range):
|
||||
SyncDepthScale();
|
||||
@ -1280,6 +1294,20 @@ void RasterizerOpenGL::SetShader() {
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncClipEnabled() {
|
||||
state.clip_distance[1] = Pica::g_state.regs.rasterizer.clip_enable != 0;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncClipCoef() {
|
||||
const auto raw_clip_coef = Pica::g_state.regs.rasterizer.GetClipCoef();
|
||||
const GLvec4 new_clip_coef = {raw_clip_coef.x.ToFloat32(), raw_clip_coef.y.ToFloat32(),
|
||||
raw_clip_coef.z.ToFloat32(), raw_clip_coef.w.ToFloat32()};
|
||||
if (new_clip_coef != uniform_block_data.data.clip_coef) {
|
||||
uniform_block_data.data.clip_coef = new_clip_coef;
|
||||
uniform_block_data.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncCullMode() {
|
||||
const auto& regs = Pica::g_state.regs;
|
||||
|
||||
|
@ -151,14 +151,21 @@ private:
|
||||
LightSrc light_src[8];
|
||||
alignas(16) GLvec4 const_color[6]; // A vec4 color for each of the six tev stages
|
||||
alignas(16) GLvec4 tev_combiner_buffer_color;
|
||||
alignas(16) GLvec4 clip_coef;
|
||||
};
|
||||
|
||||
static_assert(
|
||||
sizeof(UniformData) == 0x460,
|
||||
sizeof(UniformData) == 0x470,
|
||||
"The size of the UniformData structure has changed, update the structure in the shader");
|
||||
static_assert(sizeof(UniformData) < 16384,
|
||||
"UniformData structure must be less than 16kb as per the OpenGL spec");
|
||||
|
||||
/// Syncs the clip enabled status to match the PICA register
|
||||
void SyncClipEnabled();
|
||||
|
||||
/// Syncs the clip coefficients to match the PICA register
|
||||
void SyncClipCoef();
|
||||
|
||||
/// Sets the OpenGL shader in accordance with the current PICA register state
|
||||
void SetShader();
|
||||
|
||||
|
@ -25,6 +25,42 @@ using TevStageConfig = TexturingRegs::TevStageConfig;
|
||||
|
||||
namespace GLShader {
|
||||
|
||||
static const std::string UniformBlockDef = R"(
|
||||
#define NUM_TEV_STAGES 6
|
||||
#define NUM_LIGHTS 8
|
||||
|
||||
struct LightSrc {
|
||||
vec3 specular_0;
|
||||
vec3 specular_1;
|
||||
vec3 diffuse;
|
||||
vec3 ambient;
|
||||
vec3 position;
|
||||
vec3 spot_direction;
|
||||
float dist_atten_bias;
|
||||
float dist_atten_scale;
|
||||
};
|
||||
|
||||
layout (std140) uniform shader_data {
|
||||
vec2 framebuffer_scale;
|
||||
int alphatest_ref;
|
||||
float depth_scale;
|
||||
float depth_offset;
|
||||
int scissor_x1;
|
||||
int scissor_y1;
|
||||
int scissor_x2;
|
||||
int scissor_y2;
|
||||
vec3 fog_color;
|
||||
vec2 proctex_noise_f;
|
||||
vec2 proctex_noise_a;
|
||||
vec2 proctex_noise_p;
|
||||
vec3 lighting_global_ambient;
|
||||
LightSrc light_src[NUM_LIGHTS];
|
||||
vec4 const_color[NUM_TEV_STAGES];
|
||||
vec4 tev_combiner_buffer_color;
|
||||
vec4 clip_coef;
|
||||
};
|
||||
)";
|
||||
|
||||
PicaShaderConfig PicaShaderConfig::BuildFromRegs(const Pica::Regs& regs) {
|
||||
PicaShaderConfig res;
|
||||
|
||||
@ -1010,8 +1046,6 @@ std::string GenerateFragmentShader(const PicaShaderConfig& config) {
|
||||
|
||||
std::string out = R"(
|
||||
#version 330 core
|
||||
#define NUM_TEV_STAGES 6
|
||||
#define NUM_LIGHTS 8
|
||||
|
||||
in vec4 primary_color;
|
||||
in vec2 texcoord[3];
|
||||
@ -1023,36 +1057,6 @@ in vec4 gl_FragCoord;
|
||||
|
||||
out vec4 color;
|
||||
|
||||
struct LightSrc {
|
||||
vec3 specular_0;
|
||||
vec3 specular_1;
|
||||
vec3 diffuse;
|
||||
vec3 ambient;
|
||||
vec3 position;
|
||||
vec3 spot_direction;
|
||||
float dist_atten_bias;
|
||||
float dist_atten_scale;
|
||||
};
|
||||
|
||||
layout (std140) uniform shader_data {
|
||||
vec2 framebuffer_scale;
|
||||
int alphatest_ref;
|
||||
float depth_scale;
|
||||
float depth_offset;
|
||||
int scissor_x1;
|
||||
int scissor_y1;
|
||||
int scissor_x2;
|
||||
int scissor_y2;
|
||||
vec3 fog_color;
|
||||
vec2 proctex_noise_f;
|
||||
vec2 proctex_noise_a;
|
||||
vec2 proctex_noise_p;
|
||||
vec3 lighting_global_ambient;
|
||||
LightSrc light_src[NUM_LIGHTS];
|
||||
vec4 const_color[NUM_TEV_STAGES];
|
||||
vec4 tev_combiner_buffer_color;
|
||||
};
|
||||
|
||||
uniform sampler2D tex[3];
|
||||
uniform samplerBuffer lighting_lut;
|
||||
uniform samplerBuffer fog_lut;
|
||||
@ -1061,7 +1065,11 @@ uniform samplerBuffer proctex_color_map;
|
||||
uniform samplerBuffer proctex_alpha_map;
|
||||
uniform samplerBuffer proctex_lut;
|
||||
uniform samplerBuffer proctex_diff_lut;
|
||||
)";
|
||||
|
||||
out += UniformBlockDef;
|
||||
|
||||
out += R"(
|
||||
// Rotate the vector v by the quaternion q
|
||||
vec3 quaternion_rotate(vec4 q, vec3 v) {
|
||||
return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v);
|
||||
@ -1197,6 +1205,12 @@ out float texcoord0_w;
|
||||
out vec4 normquat;
|
||||
out vec3 view;
|
||||
|
||||
)";
|
||||
|
||||
out += UniformBlockDef;
|
||||
|
||||
out += R"(
|
||||
|
||||
void main() {
|
||||
primary_color = vert_color;
|
||||
texcoord[0] = vert_texcoord0;
|
||||
@ -1207,7 +1221,7 @@ void main() {
|
||||
view = vert_view;
|
||||
gl_Position = vert_position;
|
||||
gl_ClipDistance[0] = -vert_position.z; // fixed PICA clipping plane z <= 0
|
||||
// TODO (wwylele): calculate gl_ClipDistance[1] from user-defined clipping plane
|
||||
gl_ClipDistance[1] = dot(clip_coef, vert_position);
|
||||
}
|
||||
)";
|
||||
|
||||
|
@ -52,7 +52,8 @@ OutputVertex OutputVertex::FromAttributeBuffer(const RasterizerRegs& regs,
|
||||
// The hardware takes the absolute and saturates vertex colors like this, *before* doing
|
||||
// interpolation
|
||||
for (unsigned i = 0; i < 4; ++i) {
|
||||
ret.color[i] = float24::FromFloat32(std::fmin(std::fabs(ret.color[i].ToFloat32()), 1.0f));
|
||||
float c = std::fabs(ret.color[i].ToFloat32());
|
||||
ret.color[i] = float24::FromFloat32(c < 1.0f ? c : 1.0f);
|
||||
}
|
||||
|
||||
LOG_TRACE(HW_GPU, "Output vertex: pos(%.2f, %.2f, %.2f, %.2f), quat(%.2f, %.2f, %.2f, %.2f), "
|
||||
|
@ -31,7 +31,7 @@ public:
|
||||
: coeffs(coeffs), bias(bias) {}
|
||||
|
||||
bool IsInside(const Vertex& vertex) const {
|
||||
return Math::Dot(vertex.pos + bias, coeffs) <= float24::FromFloat32(0);
|
||||
return Math::Dot(vertex.pos + bias, coeffs) >= float24::FromFloat32(0);
|
||||
}
|
||||
|
||||
bool IsOutSide(const Vertex& vertex) const {
|
||||
@ -116,19 +116,18 @@ void ProcessTriangle(const OutputVertex& v0, const OutputVertex& v1, const Outpu
|
||||
static const float24 f0 = float24::FromFloat32(0.0);
|
||||
static const float24 f1 = float24::FromFloat32(1.0);
|
||||
static const std::array<ClippingEdge, 7> clipping_edges = {{
|
||||
{Math::MakeVec(f1, f0, f0, -f1)}, // x = +w
|
||||
{Math::MakeVec(-f1, f0, f0, -f1)}, // x = -w
|
||||
{Math::MakeVec(f0, f1, f0, -f1)}, // y = +w
|
||||
{Math::MakeVec(f0, -f1, f0, -f1)}, // y = -w
|
||||
{Math::MakeVec(f0, f0, f1, f0)}, // z = 0
|
||||
{Math::MakeVec(f0, f0, -f1, -f1)}, // z = -w
|
||||
{Math::MakeVec(f0, f0, f0, -f1), Math::Vec4<float24>(f0, f0, f0, EPSILON)}, // w = EPSILON
|
||||
{Math::MakeVec(-f1, f0, f0, f1)}, // x = +w
|
||||
{Math::MakeVec(f1, f0, f0, f1)}, // x = -w
|
||||
{Math::MakeVec(f0, -f1, f0, f1)}, // y = +w
|
||||
{Math::MakeVec(f0, f1, f0, f1)}, // y = -w
|
||||
{Math::MakeVec(f0, f0, -f1, f0)}, // z = 0
|
||||
{Math::MakeVec(f0, f0, f1, f1)}, // z = -w
|
||||
{Math::MakeVec(f0, f0, f0, f1), Math::Vec4<float24>(f0, f0, f0, EPSILON)}, // w = EPSILON
|
||||
}};
|
||||
|
||||
// Simple implementation of the Sutherland-Hodgman clipping algorithm.
|
||||
// TODO: Make this less inefficient (currently lots of useless buffering overhead happens here)
|
||||
for (auto edge : clipping_edges) {
|
||||
|
||||
auto Clip = [&](const ClippingEdge& edge) {
|
||||
std::swap(input_list, output_list);
|
||||
output_list->clear();
|
||||
|
||||
@ -147,12 +146,24 @@ void ProcessTriangle(const OutputVertex& v0, const OutputVertex& v1, const Outpu
|
||||
}
|
||||
reference_vertex = &vertex;
|
||||
}
|
||||
};
|
||||
|
||||
for (auto edge : clipping_edges) {
|
||||
Clip(edge);
|
||||
|
||||
// Need to have at least a full triangle to continue...
|
||||
if (output_list->size() < 3)
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_state.regs.rasterizer.clip_enable) {
|
||||
ClippingEdge custom_edge{g_state.regs.rasterizer.GetClipCoef()};
|
||||
Clip(custom_edge);
|
||||
|
||||
if (output_list->size() < 3)
|
||||
return;
|
||||
}
|
||||
|
||||
InitScreenCoordinates((*output_list)[0]);
|
||||
InitScreenCoordinates((*output_list)[1]);
|
||||
|
||||
|
@ -1,10 +1,12 @@
|
||||
set(SRCS
|
||||
telemetry_json.cpp
|
||||
verify_login.cpp
|
||||
web_backend.cpp
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
telemetry_json.h
|
||||
verify_login.h
|
||||
web_backend.h
|
||||
)
|
||||
|
||||
|
28
src/web_service/verify_login.cpp
Normal file
28
src/web_service/verify_login.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <json.hpp>
|
||||
#include "web_service/verify_login.h"
|
||||
#include "web_service/web_backend.h"
|
||||
|
||||
namespace WebService {
|
||||
|
||||
std::future<bool> VerifyLogin(std::string& username, std::string& token,
|
||||
const std::string& endpoint_url, std::function<void()> func) {
|
||||
auto get_func = [func, username](const std::string& reply) -> bool {
|
||||
func();
|
||||
if (reply.empty())
|
||||
return false;
|
||||
nlohmann::json json = nlohmann::json::parse(reply);
|
||||
std::string result;
|
||||
try {
|
||||
result = json["username"];
|
||||
} catch (const nlohmann::detail::out_of_range&) {
|
||||
}
|
||||
return result == username;
|
||||
};
|
||||
return GetJson<bool>(get_func, endpoint_url, false, username, token);
|
||||
}
|
||||
|
||||
} // namespace WebService
|
24
src/web_service/verify_login.h
Normal file
24
src/web_service/verify_login.h
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <string>
|
||||
|
||||
namespace WebService {
|
||||
|
||||
/**
|
||||
* Checks if username and token is valid
|
||||
* @param username Citra username to use for authentication.
|
||||
* @param token Citra token to use for authentication.
|
||||
* @param endpoint_url URL of the services.citra-emu.org endpoint.
|
||||
* @param func A function that gets exectued when the verification is finished
|
||||
* @returns Future with bool indicating whether the verification succeeded
|
||||
*/
|
||||
std::future<bool> VerifyLogin(std::string& username, std::string& token,
|
||||
const std::string& endpoint_url, std::function<void()> func);
|
||||
|
||||
} // namespace WebService
|
@ -18,6 +18,19 @@ static constexpr char API_VERSION[]{"1"};
|
||||
|
||||
static std::unique_ptr<cpr::Session> g_session;
|
||||
|
||||
void Win32WSAStartup() {
|
||||
#ifdef _WIN32
|
||||
// On Windows, CPR/libcurl does not properly initialize Winsock. The below code is used to
|
||||
// initialize Winsock globally, which fixes this problem. Without this, only the first CPR
|
||||
// session will properly be created, and subsequent ones will fail.
|
||||
WSADATA wsa_data;
|
||||
const int wsa_result{WSAStartup(MAKEWORD(2, 2), &wsa_data)};
|
||||
if (wsa_result) {
|
||||
LOG_CRITICAL(WebService, "WSAStartup failed: %d", wsa_result);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void PostJson(const std::string& url, const std::string& data, bool allow_anonymous,
|
||||
const std::string& username, const std::string& token) {
|
||||
if (url.empty()) {
|
||||
@ -31,16 +44,7 @@ void PostJson(const std::string& url, const std::string& data, bool allow_anonym
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// On Windows, CPR/libcurl does not properly initialize Winsock. The below code is used to
|
||||
// initialize Winsock globally, which fixes this problem. Without this, only the first CPR
|
||||
// session will properly be created, and subsequent ones will fail.
|
||||
WSADATA wsa_data;
|
||||
const int wsa_result{WSAStartup(MAKEWORD(2, 2), &wsa_data)};
|
||||
if (wsa_result) {
|
||||
LOG_CRITICAL(WebService, "WSAStartup failed: %d", wsa_result);
|
||||
}
|
||||
#endif
|
||||
Win32WSAStartup();
|
||||
|
||||
// Built request header
|
||||
cpr::Header header;
|
||||
@ -56,8 +60,81 @@ void PostJson(const std::string& url, const std::string& data, bool allow_anonym
|
||||
}
|
||||
|
||||
// Post JSON asynchronously
|
||||
static cpr::AsyncResponse future;
|
||||
future = cpr::PostAsync(cpr::Url{url.c_str()}, cpr::Body{data.c_str()}, header);
|
||||
static std::future<void> future;
|
||||
future = cpr::PostCallback(
|
||||
[](cpr::Response r) {
|
||||
if (r.error) {
|
||||
LOG_ERROR(WebService, "POST returned cpr error: %u:%s",
|
||||
static_cast<u32>(r.error.code), r.error.message.c_str());
|
||||
return;
|
||||
}
|
||||
if (r.status_code >= 400) {
|
||||
LOG_ERROR(WebService, "POST returned error status code: %u", r.status_code);
|
||||
return;
|
||||
}
|
||||
if (r.header["content-type"].find("application/json") == std::string::npos) {
|
||||
LOG_ERROR(WebService, "POST returned wrong content: %s",
|
||||
r.header["content-type"].c_str());
|
||||
return;
|
||||
}
|
||||
},
|
||||
cpr::Url{url}, cpr::Body{data}, header);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::future<T> GetJson(std::function<T(const std::string&)> func, const std::string& url,
|
||||
bool allow_anonymous, const std::string& username,
|
||||
const std::string& token) {
|
||||
if (url.empty()) {
|
||||
LOG_ERROR(WebService, "URL is invalid");
|
||||
return std::async(std::launch::async, [func{std::move(func)}]() { return func(""); });
|
||||
}
|
||||
|
||||
const bool are_credentials_provided{!token.empty() && !username.empty()};
|
||||
if (!allow_anonymous && !are_credentials_provided) {
|
||||
LOG_ERROR(WebService, "Credentials must be provided for authenticated requests");
|
||||
return std::async(std::launch::async, [func{std::move(func)}]() { return func(""); });
|
||||
}
|
||||
|
||||
Win32WSAStartup();
|
||||
|
||||
// Built request header
|
||||
cpr::Header header;
|
||||
if (are_credentials_provided) {
|
||||
// Authenticated request if credentials are provided
|
||||
header = {{"Content-Type", "application/json"},
|
||||
{"x-username", username.c_str()},
|
||||
{"x-token", token.c_str()},
|
||||
{"api-version", API_VERSION}};
|
||||
} else {
|
||||
// Otherwise, anonymous request
|
||||
header = cpr::Header{{"Content-Type", "application/json"}, {"api-version", API_VERSION}};
|
||||
}
|
||||
|
||||
// Get JSON asynchronously
|
||||
return cpr::GetCallback(
|
||||
[func{std::move(func)}](cpr::Response r) {
|
||||
if (r.error) {
|
||||
LOG_ERROR(WebService, "GET returned cpr error: %u:%s",
|
||||
static_cast<u32>(r.error.code), r.error.message.c_str());
|
||||
return func("");
|
||||
}
|
||||
if (r.status_code >= 400) {
|
||||
LOG_ERROR(WebService, "GET returned error code: %u", r.status_code);
|
||||
return func("");
|
||||
}
|
||||
if (r.header["content-type"].find("application/json") == std::string::npos) {
|
||||
LOG_ERROR(WebService, "GET returned wrong content: %s",
|
||||
r.header["content-type"].c_str());
|
||||
return func("");
|
||||
}
|
||||
return func(r.text);
|
||||
},
|
||||
cpr::Url{url}, header);
|
||||
}
|
||||
|
||||
template std::future<bool> GetJson(std::function<bool(const std::string&)> func,
|
||||
const std::string& url, bool allow_anonymous,
|
||||
const std::string& username, const std::string& token);
|
||||
|
||||
} // namespace WebService
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
|
||||
@ -20,4 +22,18 @@ namespace WebService {
|
||||
void PostJson(const std::string& url, const std::string& data, bool allow_anonymous,
|
||||
const std::string& username = {}, const std::string& token = {});
|
||||
|
||||
/**
|
||||
* Gets JSON from services.citra-emu.org.
|
||||
* @param func A function that gets exectued when the json as a string is received
|
||||
* @param url URL of the services.citra-emu.org endpoint to post data to.
|
||||
* @param allow_anonymous If true, allow anonymous unauthenticated requests.
|
||||
* @param username Citra username to use for authentication.
|
||||
* @param token Citra token to use for authentication.
|
||||
* @return future that holds the return value T of the func
|
||||
*/
|
||||
template <typename T>
|
||||
std::future<T> GetJson(std::function<T(const std::string&)> func, const std::string& url,
|
||||
bool allow_anonymous, const std::string& username = {},
|
||||
const std::string& token = {});
|
||||
|
||||
} // namespace WebService
|
||||
|
Loading…
Reference in New Issue
Block a user