mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-24 14:01:05 +00:00
Separate out http_c into multiple files under http/
Also implement safe thread exiting, and closing of contexts.
This commit is contained in:
parent
24cc45c246
commit
779b482ac3
@ -65,7 +65,8 @@ set(SRCS
|
||||
hle/service/hid/hid.cpp
|
||||
hle/service/hid/hid_spvr.cpp
|
||||
hle/service/hid/hid_user.cpp
|
||||
hle/service/http_c.cpp
|
||||
hle/service/http/http.cpp
|
||||
hle/service/http/http_c.cpp
|
||||
hle/service/ir/ir.cpp
|
||||
hle/service/ir/ir_rst.cpp
|
||||
hle/service/ir/ir_u.cpp
|
||||
@ -177,7 +178,8 @@ set(HEADERS
|
||||
hle/service/hid/hid.h
|
||||
hle/service/hid/hid_spvr.h
|
||||
hle/service/hid/hid_user.h
|
||||
hle/service/http_c.h
|
||||
hle/service/http/http.h
|
||||
hle/service/http/http_c.h
|
||||
hle/service/ir/ir.h
|
||||
hle/service/ir/ir_rst.h
|
||||
hle/service/ir/ir_u.h
|
||||
|
119
src/core/hle/service/http/http.cpp
Normal file
119
src/core/hle/service/http/http.cpp
Normal file
@ -0,0 +1,119 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "common/string_util.h"
|
||||
|
||||
#include "core/hle/service/http/http.h"
|
||||
#include "core/hle/service/http/http_c.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service {
|
||||
namespace HTTP {
|
||||
|
||||
std::unordered_map<ContextHandle, std::unique_ptr<HttpContext>> context_map;
|
||||
ContextHandle next_handle;
|
||||
|
||||
static int WriteBuf(u8 *data, size_t size, size_t nmemb, std::mutex *mutex, std::vector<u8> *buffer) {
|
||||
std::lock_guard<std::mutex> lock(*mutex);
|
||||
buffer->reserve(buffer->size() + size * nmemb);
|
||||
buffer->insert(buffer->end(), data, data + size * nmemb);
|
||||
return static_cast<int>(size * nmemb);
|
||||
}
|
||||
|
||||
static int HdrWriter(u8 *data, size_t size, size_t nmemb, HttpContext *context) {
|
||||
if (context == nullptr || context->should_quit)
|
||||
return -1;
|
||||
return WriteBuf(data, size, nmemb, &context->mutex, &context->response_hdrs);
|
||||
}
|
||||
|
||||
static int DataWriter(u8 *data, size_t size, size_t nmemb, HttpContext *context) {
|
||||
if (context == nullptr || context->should_quit)
|
||||
return -1;
|
||||
return WriteBuf(data, size, nmemb, &context->mutex, &context->response_data);
|
||||
}
|
||||
|
||||
HttpContext::~HttpContext() {
|
||||
curl_slist_free_all(request_hdrs);
|
||||
}
|
||||
|
||||
void MakeRequest(HttpContext* context) {
|
||||
CURL* connection = curl_easy_init();
|
||||
CURLcode res;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(context->mutex);
|
||||
|
||||
context->state = REQUEST_STATE_IN_PROGRESS;
|
||||
res = curl_easy_setopt(connection, CURLOPT_URL, context->url.c_str());
|
||||
switch (context->req_type) {
|
||||
case REQUEST_TYPE_GET:
|
||||
res = curl_easy_setopt(connection, CURLOPT_HTTPGET, 1);
|
||||
break;
|
||||
case REQUEST_TYPE_POST:
|
||||
case REQUEST_TYPE_POST_:
|
||||
res = curl_easy_setopt(connection, CURLOPT_POST, 1);
|
||||
break;
|
||||
case REQUEST_TYPE_PUT:
|
||||
case REQUEST_TYPE_PUT_:
|
||||
res = curl_easy_setopt(connection, CURLOPT_UPLOAD, 1);
|
||||
break;
|
||||
case REQUEST_TYPE_DELETE:
|
||||
// TODO
|
||||
break;
|
||||
case REQUEST_TYPE_HEAD:
|
||||
res = curl_easy_setopt(connection, CURLOPT_NOBODY, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
res = curl_easy_setopt(connection, CURLOPT_HEADERFUNCTION, HdrWriter);
|
||||
res = curl_easy_setopt(connection, CURLOPT_WRITEFUNCTION, DataWriter);
|
||||
res = curl_easy_setopt(connection, CURLOPT_HEADERDATA, context);
|
||||
res = curl_easy_setopt(connection, CURLOPT_WRITEDATA, context);
|
||||
res = curl_easy_setopt(connection, CURLOPT_HTTPHEADER, context->request_hdrs);
|
||||
}
|
||||
|
||||
res = curl_easy_perform(connection);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(context->mutex);
|
||||
|
||||
res = curl_easy_getinfo(connection, CURLINFO_RESPONSE_CODE, &context->response_code);
|
||||
res = curl_easy_getinfo(connection, CURLINFO_SIZE_DOWNLOAD, &context->downloaded_size);
|
||||
res = curl_easy_getinfo(connection, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &context->download_size);
|
||||
|
||||
context->state = REQUEST_STATE_READY;
|
||||
}
|
||||
|
||||
curl_easy_cleanup(connection);
|
||||
}
|
||||
|
||||
void AddRequestHeader(std::string name, std::string value, curl_slist** hdr_list) {
|
||||
std::string header = Common::StringFromFormat("%s: %s", name.c_str(), value.c_str());
|
||||
*hdr_list = curl_slist_append(*hdr_list, header.c_str());
|
||||
}
|
||||
|
||||
void Init() {
|
||||
AddService(new HTTP_C_Interface);
|
||||
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
}
|
||||
|
||||
void ClearInstance() {
|
||||
for (auto& pair : context_map) {
|
||||
pair.second->should_quit = true;
|
||||
if (pair.second->req_thread != nullptr)
|
||||
pair.second->req_thread->join();
|
||||
}
|
||||
|
||||
context_map.clear();
|
||||
next_handle = 0;
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
curl_global_cleanup();
|
||||
ClearInstance();
|
||||
}
|
||||
|
||||
} // namespace HTTP
|
||||
} // namespace Service
|
74
src/core/hle/service/http/http.h
Normal file
74
src/core/hle/service/http/http.h
Normal file
@ -0,0 +1,74 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "core/hle/result.h"
|
||||
|
||||
struct curl_slist;
|
||||
|
||||
namespace Service {
|
||||
namespace HTTP {
|
||||
|
||||
typedef u32 ContextHandle;
|
||||
|
||||
enum RequestType : u32 {
|
||||
REQUEST_TYPE_NONE = 0,
|
||||
REQUEST_TYPE_GET = 1,
|
||||
REQUEST_TYPE_POST = 2,
|
||||
REQUEST_TYPE_HEAD = 3,
|
||||
REQUEST_TYPE_PUT = 4,
|
||||
REQUEST_TYPE_DELETE = 5,
|
||||
REQUEST_TYPE_POST_ = 6,
|
||||
REQUEST_TYPE_PUT_ = 7
|
||||
};
|
||||
|
||||
enum RequestState : u32 {
|
||||
REQUEST_STATE_DEFAULT, // TODO
|
||||
REQUEST_STATE_IN_PROGRESS = 5,
|
||||
REQUEST_STATE_READY = 8,
|
||||
};
|
||||
|
||||
struct HttpContext {
|
||||
std::mutex mutex;
|
||||
bool should_quit;
|
||||
|
||||
RequestState state;
|
||||
std::unique_ptr<std::thread> req_thread;
|
||||
|
||||
std::string url;
|
||||
RequestType req_type;
|
||||
curl_slist* request_hdrs;
|
||||
|
||||
std::vector<u8> response_hdrs;
|
||||
std::vector<u8> response_data;
|
||||
long response_code;
|
||||
double download_size;
|
||||
double downloaded_size;
|
||||
|
||||
~HttpContext();
|
||||
};
|
||||
|
||||
extern std::unordered_map<ContextHandle, std::unique_ptr<HttpContext>> context_map;
|
||||
extern ContextHandle next_handle;
|
||||
|
||||
void MakeRequest(HttpContext* context);
|
||||
void AddRequestHeader(std::string name, std::string value, curl_slist** hdr_list);
|
||||
|
||||
/// Initialize the HTTP service
|
||||
void Init();
|
||||
|
||||
void ClearInstance();
|
||||
|
||||
/// Shutdown the HTTP service
|
||||
void Shutdown();
|
||||
|
||||
} // namespace HTTP
|
||||
} // namespace Service
|
@ -4,87 +4,21 @@
|
||||
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "core/hle/hle.h"
|
||||
#include "core/hle/service/http_c.h"
|
||||
#include "core/hle/service/http/http.h"
|
||||
#include "core/hle/service/http/http_c.h"
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/make_unique.h"
|
||||
#include "common/string_util.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Namespace HTTP_C
|
||||
|
||||
namespace HTTP_C {
|
||||
|
||||
typedef u32 ContextHandle;
|
||||
|
||||
enum RequestType : u32 {
|
||||
REQUEST_TYPE_NONE = 0,
|
||||
REQUEST_TYPE_GET = 1,
|
||||
REQUEST_TYPE_POST = 2,
|
||||
REQUEST_TYPE_HEAD = 3,
|
||||
REQUEST_TYPE_PUT = 4,
|
||||
REQUEST_TYPE_DELETE = 5,
|
||||
REQUEST_TYPE_POST_ = 6,
|
||||
REQUEST_TYPE_PUT_ = 7
|
||||
};
|
||||
|
||||
enum RequestState : u32 {
|
||||
REQUEST_STATE_DEFAULT, // TODO
|
||||
REQUEST_STATE_IN_PROGRESS = 5,
|
||||
REQUEST_STATE_READY = 7,
|
||||
};
|
||||
|
||||
struct HttpContext {
|
||||
std::mutex mutex;
|
||||
|
||||
RequestState state;
|
||||
|
||||
std::string url;
|
||||
RequestType req_type;
|
||||
curl_slist* request_hdrs;
|
||||
|
||||
std::vector<u8> response_hdrs;
|
||||
std::vector<u8> response_data;
|
||||
long response_code;
|
||||
double download_size;
|
||||
double downloaded_size;
|
||||
|
||||
~HttpContext() {
|
||||
curl_slist_free_all(request_hdrs);
|
||||
}
|
||||
};
|
||||
|
||||
static std::unordered_map<ContextHandle, std::unique_ptr<HttpContext>> context_map;
|
||||
static ContextHandle next_handle;
|
||||
|
||||
static int writer_hdrs(u8* data, size_t size, size_t nmemb, HttpContext* context) {
|
||||
if (context == nullptr)
|
||||
return 0;
|
||||
std::lock_guard<std::mutex> lock(context->mutex);
|
||||
context->response_hdrs.reserve(context->response_hdrs.size() + size * nmemb);
|
||||
context->response_hdrs.insert(context->response_hdrs.end(), data, data + size * nmemb);
|
||||
return (int)(size * nmemb);
|
||||
}
|
||||
|
||||
static int writer_data(u8* data, size_t size, size_t nmemb, HttpContext* context) {
|
||||
if (context == nullptr)
|
||||
return 0;
|
||||
std::lock_guard<std::mutex> lock(context->mutex);
|
||||
context->response_data.reserve(context->response_data.size() + size * nmemb);
|
||||
context->response_data.insert(context->response_data.end(), data, data + size * nmemb);
|
||||
return (int)(size * nmemb);
|
||||
}
|
||||
namespace Service {
|
||||
namespace HTTP {
|
||||
|
||||
static void Initialize(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
}
|
||||
|
||||
@ -105,7 +39,6 @@ static void CreateContext(Service::Interface* self) {
|
||||
|
||||
context->req_type = req_type;
|
||||
context->url = url;
|
||||
|
||||
context_map.emplace(next_handle, std::move(context));
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
@ -118,17 +51,36 @@ static void CloseContext(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
ContextHandle handle = static_cast<ContextHandle>(cmd_buff[1]);
|
||||
// auto map_it = request_map.find(handle);
|
||||
// if (map_it == request_map.end()) {
|
||||
// cmd_buff[1] = -1; // TODO: Find proper result code for invalid handle
|
||||
// return;
|
||||
// }
|
||||
// TODO: wait for thread to finish!
|
||||
auto map_it = context_map.find(handle);
|
||||
if (map_it == context_map.end()) {
|
||||
cmd_buff[1] = -1; // TODO: Find proper result code for invalid handle
|
||||
return;
|
||||
}
|
||||
|
||||
map_it->second->should_quit = true;
|
||||
if (map_it->second->req_thread != nullptr)
|
||||
map_it->second->req_thread->join();
|
||||
|
||||
context_map.erase(handle);
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
}
|
||||
|
||||
static void CancelConnection(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
ContextHandle handle = static_cast<ContextHandle>(cmd_buff[1]);
|
||||
|
||||
auto map_it = context_map.find(handle);
|
||||
if (map_it == context_map.end()) {
|
||||
cmd_buff[1] = -1; // TODO: Find proper result code for invalid handle
|
||||
return;
|
||||
}
|
||||
map_it->second->should_quit = true;
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
}
|
||||
|
||||
static void GetRequestState(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
@ -169,56 +121,6 @@ static void GetDownloadSizeState(Service::Interface* self) {
|
||||
return;
|
||||
}
|
||||
|
||||
static void MakeRequest(HttpContext* context) {
|
||||
CURL* connection = curl_easy_init();
|
||||
CURLcode res;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(context->mutex);
|
||||
|
||||
context->state = REQUEST_STATE_IN_PROGRESS;
|
||||
res = curl_easy_setopt(connection, CURLOPT_URL, context->url.c_str());
|
||||
switch (context->req_type) {
|
||||
case REQUEST_TYPE_GET:
|
||||
res = curl_easy_setopt(connection, CURLOPT_HTTPGET, 1);
|
||||
break;
|
||||
case REQUEST_TYPE_POST:
|
||||
case REQUEST_TYPE_POST_:
|
||||
res = curl_easy_setopt(connection, CURLOPT_POST, 1);
|
||||
break;
|
||||
case REQUEST_TYPE_PUT:
|
||||
case REQUEST_TYPE_PUT_:
|
||||
res = curl_easy_setopt(connection, CURLOPT_UPLOAD, 1);
|
||||
break;
|
||||
case REQUEST_TYPE_DELETE:
|
||||
// TODO
|
||||
break;
|
||||
case REQUEST_TYPE_HEAD:
|
||||
res = curl_easy_setopt(connection, CURLOPT_NOBODY, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
res = curl_easy_setopt(connection, CURLOPT_HEADERFUNCTION, writer_hdrs);
|
||||
res = curl_easy_setopt(connection, CURLOPT_WRITEFUNCTION, writer_data);
|
||||
res = curl_easy_setopt(connection, CURLOPT_HEADERDATA, context);
|
||||
res = curl_easy_setopt(connection, CURLOPT_WRITEDATA, context);
|
||||
|
||||
curl_easy_setopt(connection, CURLOPT_HTTPHEADER, context->request_hdrs);
|
||||
}
|
||||
|
||||
res = curl_easy_perform(connection);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(context->mutex);
|
||||
|
||||
curl_easy_getinfo(connection, CURLINFO_RESPONSE_CODE, &context->response_code);
|
||||
curl_easy_getinfo(connection, CURLINFO_SIZE_DOWNLOAD, &context->downloaded_size);
|
||||
curl_easy_getinfo(connection, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &context->download_size);
|
||||
|
||||
context->state = REQUEST_STATE_READY;
|
||||
}
|
||||
|
||||
curl_easy_cleanup(connection);
|
||||
}
|
||||
|
||||
static void BeginRequest(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
@ -231,9 +133,8 @@ static void BeginRequest(Service::Interface* self) {
|
||||
}
|
||||
|
||||
HttpContext* context = map_it->second.get();
|
||||
|
||||
std::thread req_thread(MakeRequest, context);
|
||||
req_thread.detach();
|
||||
std::lock_guard<std::mutex> lock(context->mutex);
|
||||
context->req_thread = Common::make_unique<std::thread>(MakeRequest, context);
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
}
|
||||
@ -242,7 +143,7 @@ static void ReceiveData(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
ContextHandle handle = static_cast<ContextHandle>(cmd_buff[1]);
|
||||
u32 buf_size = cmd_buff[2];
|
||||
u32 buf_size = cmd_buff[2];
|
||||
u8* buffer = Memory::GetPointer(cmd_buff[4]);
|
||||
|
||||
auto map_it = context_map.find(handle);
|
||||
@ -268,15 +169,12 @@ static void AddRequestHeader(Service::Interface* self) {
|
||||
ContextHandle handle = static_cast<ContextHandle>(cmd_buff[1]);
|
||||
u32 hdr_name_len = cmd_buff[2];
|
||||
u32 hdr_val_len = cmd_buff[3];
|
||||
u32 hdr_name_buf = cmd_buff[5];
|
||||
u32 hdr_val_buf = cmd_buff[7];
|
||||
const char* hdr_name_buf = reinterpret_cast<const char*>(Memory::GetPointer(cmd_buff[5]));
|
||||
const char* hdr_val_buf = reinterpret_cast<const char*>(Memory::GetPointer(cmd_buff[7]));
|
||||
|
||||
// TODO: something is NULL
|
||||
// TODO: header value is empty
|
||||
// TODO: header name is empty
|
||||
std::string header = Common::StringFromFormat("%s: %s",
|
||||
std::string(reinterpret_cast<const char*>(Memory::GetPointer(hdr_name_buf)), hdr_name_len).c_str(),
|
||||
std::string(reinterpret_cast<const char*>(Memory::GetPointer(hdr_val_buf)), hdr_val_len).c_str());
|
||||
|
||||
auto map_it = context_map.find(handle);
|
||||
if (map_it == context_map.end()) {
|
||||
@ -285,7 +183,9 @@ static void AddRequestHeader(Service::Interface* self) {
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(map_it->second->mutex);
|
||||
map_it->second->request_hdrs = curl_slist_append(map_it->second->request_hdrs, header.c_str());
|
||||
AddRequestHeader(std::string(hdr_name_buf, hdr_name_len).c_str(),
|
||||
std::string(hdr_val_buf, hdr_val_len).c_str(),
|
||||
&map_it->second->request_hdrs);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
}
|
||||
|
||||
@ -300,6 +200,7 @@ static void GetResponseStatusCode(Service::Interface* self) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Verify behavior
|
||||
while (true) {
|
||||
std::lock_guard<std::mutex> lock(map_it->second->mutex);
|
||||
if (map_it->second->state == REQUEST_STATE_READY)
|
||||
@ -317,9 +218,7 @@ static void GetResponseStatusCode(Service::Interface* self) {
|
||||
static void Finalize(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
context_map.clear();
|
||||
next_handle = 0;
|
||||
curl_global_cleanup();
|
||||
ClearInstance();
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
}
|
||||
@ -328,7 +227,7 @@ const Interface::FunctionInfo FunctionTable[] = {
|
||||
{0x00010044, Initialize, "Initialize"},
|
||||
{0x00020082, CreateContext, "CreateContext"},
|
||||
{0x00030040, CloseContext, "CloseContext"},
|
||||
{0x00040040, nullptr, "CancelConnection"},
|
||||
{0x00040040, CancelConnection, "CancelConnection"},
|
||||
{0x00050040, GetRequestState, "GetRequestState"},
|
||||
{0x00060040, GetDownloadSizeState, "GetDownloadSizeState"},
|
||||
{0x00070040, nullptr, "GetRequestError"},
|
||||
@ -370,8 +269,9 @@ const Interface::FunctionInfo FunctionTable[] = {
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Interface class
|
||||
|
||||
Interface::Interface() {
|
||||
HTTP_C_Interface::HTTP_C_Interface() {
|
||||
Register(FunctionTable);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
@ -6,14 +6,12 @@
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Namespace HTTP_C
|
||||
namespace Service {
|
||||
namespace HTTP {
|
||||
|
||||
namespace HTTP_C {
|
||||
|
||||
class Interface : public Service::Interface {
|
||||
class HTTP_C_Interface : public Service::Interface {
|
||||
public:
|
||||
Interface();
|
||||
HTTP_C_Interface();
|
||||
|
||||
std::string GetPortName() const override {
|
||||
return "http:C";
|
||||
@ -21,3 +19,4 @@ public:
|
||||
};
|
||||
|
||||
} // namespace
|
||||
}
|
@ -23,7 +23,6 @@
|
||||
#include "core/hle/service/frd_u.h"
|
||||
#include "core/hle/service/gsp_gpu.h"
|
||||
#include "core/hle/service/gsp_lcd.h"
|
||||
#include "core/hle/service/http_c.h"
|
||||
#include "core/hle/service/ldr_ro.h"
|
||||
#include "core/hle/service/mic_u.h"
|
||||
#include "core/hle/service/ndm_u.h"
|
||||
@ -43,6 +42,7 @@
|
||||
#include "core/hle/service/fs/archive.h"
|
||||
#include "core/hle/service/cfg/cfg.h"
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
#include "core/hle/service/http/http.h"
|
||||
#include "core/hle/service/ir/ir.h"
|
||||
#include "core/hle/service/ptm/ptm.h"
|
||||
|
||||
@ -115,6 +115,7 @@ void Init() {
|
||||
Service::APT::Init();
|
||||
Service::PTM::Init();
|
||||
Service::HID::Init();
|
||||
Service::HTTP::Init();
|
||||
Service::IR::Init();
|
||||
|
||||
AddService(new AC_U::Interface);
|
||||
@ -133,7 +134,6 @@ void Init() {
|
||||
AddService(new FRD_U::Interface);
|
||||
AddService(new GSP_GPU::Interface);
|
||||
AddService(new GSP_LCD::Interface);
|
||||
AddService(new HTTP_C::Interface);
|
||||
AddService(new LDR_RO::Interface);
|
||||
AddService(new MIC_U::Interface);
|
||||
AddService(new NDM_U::Interface);
|
||||
|
Loading…
Reference in New Issue
Block a user