summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorGravatar Zach Hilman2019-06-05 12:17:31 -0400
committerGravatar Zach Hilman2019-06-24 20:05:11 -0400
commit3898c3903e0d73be1e227e8cc109651299f3d05e (patch)
tree61c8ae075b4ae4bbe5294ea452ecfb38faf03491 /src/core
parentweb_browser: Correct structures and properly parse TLVs/ShimKind (diff)
downloadyuzu-3898c3903e0d73be1e227e8cc109651299f3d05e.tar.gz
yuzu-3898c3903e0d73be1e227e8cc109651299f3d05e.tar.xz
yuzu-3898c3903e0d73be1e227e8cc109651299f3d05e.zip
web_browser: Use function tables for execute and initialize
Allows easy handling of multiple shim types, as they have enough in common to be the same backend but not enough to share init/exec.
Diffstat (limited to 'src/core')
-rw-r--r--src/core/hle/service/am/applets/web_browser.cpp271
-rw-r--r--src/core/hle/service/am/applets/web_browser.h21
2 files changed, 285 insertions, 7 deletions
diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp
index 6918bda02..58efebf06 100644
--- a/src/core/hle/service/am/applets/web_browser.cpp
+++ b/src/core/hle/service/am/applets/web_browser.cpp
@@ -246,24 +246,25 @@ void WebBrowser::ExecuteInteractive() {
246} 246}
247 247
248void WebBrowser::Execute() { 248void WebBrowser::Execute() {
249 if (complete) 249 if (complete) {
250 return; 250 return;
251 }
251 252
252 if (status != RESULT_SUCCESS) { 253 if (status != RESULT_SUCCESS) {
253 complete = true; 254 complete = true;
254 return; 255 return;
255 } 256 }
256 257
257 frontend.OpenPage(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); }); 258 ExecuteInternal();
258} 259}
259 260
260void WebBrowser::UnpackRomFS() { 261void WebBrowser::UnpackRomFS() {
261 if (unpacked) 262 if (unpacked)
262 return; 263 return;
263 264
264 ASSERT(manual_romfs != nullptr); 265 ASSERT(offline_romfs != nullptr);
265 const auto dir = 266 const auto dir =
266 FileSys::ExtractRomFS(manual_romfs, FileSys::RomFSExtractionType::SingleDiscard); 267 FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard);
267 const auto& vfs{Core::System::GetInstance().GetFilesystem()}; 268 const auto& vfs{Core::System::GetInstance().GetFilesystem()};
268 const auto temp_dir = vfs->CreateDirectory(temporary_dir, FileSys::Mode::ReadWrite); 269 const auto temp_dir = vfs->CreateDirectory(temporary_dir, FileSys::Mode::ReadWrite);
269 FileSys::VfsRawCopyD(dir, temp_dir); 270 FileSys::VfsRawCopyD(dir, temp_dir);
@@ -274,12 +275,12 @@ void WebBrowser::UnpackRomFS() {
274void WebBrowser::Finalize() { 275void WebBrowser::Finalize() {
275 complete = true; 276 complete = true;
276 277
277 WebArgumentResult out{}; 278 WebCommonReturnValue out{};
278 out.result_code = 0; 279 out.result_code = 0;
279 out.last_url_size = 0; 280 out.last_url_size = 0;
280 281
281 std::vector<u8> data(sizeof(WebArgumentResult)); 282 std::vector<u8> data(sizeof(WebCommonReturnValue));
282 std::memcpy(data.data(), &out, sizeof(WebArgumentResult)); 283 std::memcpy(data.data(), &out, sizeof(WebCommonReturnValue));
283 284
284 broker.PushNormalDataFromApplet(IStorage{data}); 285 broker.PushNormalDataFromApplet(IStorage{data});
285 broker.SignalStateChanged(); 286 broker.SignalStateChanged();
@@ -287,4 +288,260 @@ void WebBrowser::Finalize() {
287 FileUtil::DeleteDirRecursively(temporary_dir); 288 FileUtil::DeleteDirRecursively(temporary_dir);
288} 289}
289 290
291void WebBrowser::InitializeInternal() {
292 using WebAppletInitializer = void (WebBrowser::*)();
293
294 constexpr std::array<WebAppletInitializer, SHIM_KIND_COUNT> functions{
295 nullptr, &WebBrowser::InitializeShop,
296 nullptr, &WebBrowser::InitializeOffline,
297 nullptr, nullptr,
298 nullptr, nullptr,
299 };
300
301 const auto index = static_cast<u32>(kind);
302
303 if (index > functions.size() || functions[index] == nullptr) {
304 LOG_ERROR(Service_AM, "Invalid shim_kind={:08X}", index);
305 return;
306 }
307
308 const auto function = functions[index];
309 (this->*function)();
310}
311
312void WebBrowser::ExecuteInternal() {
313 using WebAppletExecutor = void (WebBrowser::*)();
314
315 constexpr std::array<WebAppletExecutor, SHIM_KIND_COUNT> functions{
316 nullptr, &WebBrowser::ExecuteShop,
317 nullptr, &WebBrowser::ExecuteOffline,
318 nullptr, nullptr,
319 nullptr, nullptr,
320 };
321
322 const auto index = static_cast<u32>(kind);
323
324 if (index > functions.size() || functions[index] == nullptr) {
325 LOG_ERROR(Service_AM, "Invalid shim_kind={:08X}", index);
326 return;
327 }
328
329 const auto function = functions[index];
330 (this->*function)();
331}
332
333void WebBrowser::InitializeShop() {
334 if (frontend_e_commerce == nullptr) {
335 LOG_ERROR(Service_AM, "Missing ECommerce Applet frontend!");
336 status = ResultCode(-1);
337 return;
338 }
339
340 const auto user_id_data = args.find(WebArgTLVType::UserID);
341
342 user_id = std::nullopt;
343 if (user_id_data != args.end()) {
344 user_id = u128{};
345 std::memcpy(user_id->data(), user_id_data->second.data(), sizeof(u128));
346 }
347
348 const auto url = args.find(WebArgTLVType::ShopArgumentsURL);
349
350 if (url == args.end()) {
351 LOG_ERROR(Service_AM, "Missing EShop Arguments URL for initialization!");
352 status = ResultCode(-1);
353 return;
354 }
355
356 std::vector<std::string> split_query;
357 Common::SplitString(Common::StringFromFixedZeroTerminatedBuffer(
358 reinterpret_cast<const char*>(url->second.data()), url->second.size()),
359 '?', split_query);
360
361 // 2 -> Main URL '?' Query Parameters
362 // Less is missing info, More is malformed
363 if (split_query.size() != 2) {
364 LOG_ERROR(Service_AM, "EShop Arguments has more than one question mark, malformed");
365 status = ResultCode(-1);
366 return;
367 }
368
369 std::vector<std::string> queries;
370 Common::SplitString(split_query[1], '&', queries);
371
372 const auto split_single_query =
373 [](const std::string& in) -> std::pair<std::string, std::string> {
374 const auto index = in.find('=');
375 if (index == std::string::npos || index == in.size() - 1) {
376 return {in, ""};
377 }
378
379 return {in.substr(0, index), in.substr(index + 1)};
380 };
381
382 std::transform(queries.begin(), queries.end(),
383 std::inserter(shop_query, std::next(shop_query.begin())), split_single_query);
384
385 const auto scene = shop_query.find("scene");
386
387 if (scene == shop_query.end()) {
388 LOG_ERROR(Service_AM, "No scene parameter was passed via shop query!");
389 status = ResultCode(-1);
390 return;
391 }
392
393 const std::map<std::string, ShopWebTarget> target_map{
394 {"product_detail", ShopWebTarget::ApplicationInfo},
395 {"aocs", ShopWebTarget::AddOnContentList},
396 {"subscriptions", ShopWebTarget::SubscriptionList},
397 {"consumption", ShopWebTarget::ConsumableItemList},
398 {"settings", ShopWebTarget::Settings},
399 {"top", ShopWebTarget::Home},
400 };
401
402 const auto target = target_map.find(scene->second);
403 if (target == target_map.end()) {
404 LOG_ERROR(Service_AM, "Scene for shop query is invalid! (scene={})", scene->second);
405 status = ResultCode(-1);
406 return;
407 }
408
409 shop_web_target = target->second;
410
411 const auto title_id_data = shop_query.find("dst_app_id");
412 if (title_id_data != shop_query.end()) {
413 title_id = std::stoull(title_id_data->second, nullptr, 0x10);
414 }
415
416 const auto mode_data = shop_query.find("mode");
417 if (mode_data != shop_query.end()) {
418 shop_full_display = mode_data->second == "full";
419 }
420}
421
422void WebBrowser::InitializeOffline() {
423 if (args.find(WebArgTLVType::DocumentPath) == args.end() ||
424 args.find(WebArgTLVType::DocumentKind) == args.end() ||
425 args.find(WebArgTLVType::ApplicationID) == args.end()) {
426 status = ResultCode(-1);
427 LOG_ERROR(Service_AM, "Missing necessary parameters for initialization!");
428 }
429
430 const auto url_data = args[WebArgTLVType::DocumentPath];
431 filename = Common::StringFromFixedZeroTerminatedBuffer(
432 reinterpret_cast<const char*>(url_data.data()), url_data.size());
433
434 OfflineWebSource source;
435 ASSERT(args[WebArgTLVType::DocumentKind].size() >= 4);
436 std::memcpy(&source, args[WebArgTLVType::DocumentKind].data(), sizeof(OfflineWebSource));
437
438 constexpr std::array<const char*, 3> WEB_SOURCE_NAMES{
439 "manual",
440 "legal",
441 "system",
442 };
443
444 temporary_dir =
445 FileUtil::SanitizePath(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + "web_applet_" +
446 WEB_SOURCE_NAMES[static_cast<u32>(source) - 1],
447 FileUtil::DirectorySeparator::PlatformDefault);
448 FileUtil::DeleteDirRecursively(temporary_dir);
449
450 u64 title_id = 0; // 0 corresponds to current process
451 ASSERT(args[WebArgTLVType::ApplicationID].size() >= 0x8);
452 std::memcpy(&title_id, args[WebArgTLVType::ApplicationID].data(), sizeof(u64));
453 FileSys::ContentRecordType type = FileSys::ContentRecordType::Data;
454
455 switch (source) {
456 case OfflineWebSource::OfflineHtmlPage:
457 // While there is an AppID TLV field, in official SW this is always ignored.
458 title_id = 0;
459 type = FileSys::ContentRecordType::Manual;
460 break;
461 case OfflineWebSource::ApplicationLegalInformation:
462 type = FileSys::ContentRecordType::Legal;
463 break;
464 case OfflineWebSource::SystemDataPage:
465 type = FileSys::ContentRecordType::Data;
466 break;
467 }
468
469 if (title_id == 0) {
470 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
471 }
472
473 offline_romfs = GetApplicationRomFS(title_id, type);
474 if (offline_romfs == nullptr) {
475 status = ResultCode(-1);
476 LOG_ERROR(Service_AM, "Failed to find offline data for request!");
477 }
478
479 std::string path_additional_directory;
480 if (source == OfflineWebSource::OfflineHtmlPage) {
481 path_additional_directory = std::string(DIR_SEP) + "html-document";
482 }
483
484 filename =
485 FileUtil::SanitizePath(temporary_dir + path_additional_directory + DIR_SEP + filename,
486 FileUtil::DirectorySeparator::PlatformDefault);
487}
488
489void WebBrowser::ExecuteShop() {
490 const auto callback = [this]() { Finalize(); };
491
492 const auto check_optional_parameter = [this](const auto& p) {
493 if (!p.has_value()) {
494 LOG_ERROR(Service_AM, "Missing one or more necessary parameters for execution!");
495 status = ResultCode(-1);
496 return false;
497 }
498
499 return true;
500 };
501
502 switch (shop_web_target) {
503 case ShopWebTarget::ApplicationInfo:
504 if (!check_optional_parameter(title_id))
505 return;
506 frontend_e_commerce->ShowApplicationInformation(callback, *title_id, user_id,
507 shop_full_display, shop_extra_parameter);
508 break;
509 case ShopWebTarget::AddOnContentList:
510 if (!check_optional_parameter(title_id))
511 return;
512 frontend_e_commerce->ShowAddOnContentList(callback, *title_id, user_id, shop_full_display);
513 break;
514 case ShopWebTarget::ConsumableItemList:
515 if (!check_optional_parameter(title_id))
516 return;
517 frontend_e_commerce->ShowConsumableItemList(callback, *title_id, user_id);
518 break;
519 case ShopWebTarget::Home:
520 if (!check_optional_parameter(user_id))
521 return;
522 if (!check_optional_parameter(shop_full_display))
523 return;
524 frontend_e_commerce->ShowShopHome(callback, *user_id, *shop_full_display);
525 break;
526 case ShopWebTarget::Settings:
527 if (!check_optional_parameter(user_id))
528 return;
529 if (!check_optional_parameter(shop_full_display))
530 return;
531 frontend_e_commerce->ShowSettings(callback, *user_id, *shop_full_display);
532 break;
533 case ShopWebTarget::SubscriptionList:
534 if (!check_optional_parameter(title_id))
535 return;
536 frontend_e_commerce->ShowSubscriptionList(callback, *title_id, user_id);
537 break;
538 default:
539 UNREACHABLE();
540 }
541}
542
543void WebBrowser::ExecuteOffline() {
544 frontend.OpenPageLocal(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); });
545}
546
290} // namespace Service::AM::Applets 547} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/web_browser.h b/src/core/hle/service/am/applets/web_browser.h
index 2474675de..a3d2627f4 100644
--- a/src/core/hle/service/am/applets/web_browser.h
+++ b/src/core/hle/service/am/applets/web_browser.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <map>
7#include "core/file_sys/vfs_types.h" 8#include "core/file_sys/vfs_types.h"
8#include "core/hle/service/am/am.h" 9#include "core/hle/service/am/am.h"
9#include "core/hle/service/am/applets/applets.h" 10#include "core/hle/service/am/applets/applets.h"
@@ -11,6 +12,7 @@
11namespace Service::AM::Applets { 12namespace Service::AM::Applets {
12 13
13enum class ShimKind : u32; 14enum class ShimKind : u32;
15enum class ShopWebTarget;
14enum class WebArgTLVType : u16; 16enum class WebArgTLVType : u16;
15 17
16class WebBrowser final : public Applet { 18class WebBrowser final : public Applet {
@@ -35,6 +37,17 @@ public:
35 void Finalize(); 37 void Finalize();
36 38
37private: 39private:
40 void InitializeInternal();
41 void ExecuteInternal();
42
43 // Specific initializers for the types of web applets
44 void InitializeShop();
45 void InitializeOffline();
46
47 // Specific executors for the types of web applets
48 void ExecuteShop();
49 void ExecuteOffline();
50
38 Core::Frontend::WebBrowserApplet& frontend; 51 Core::Frontend::WebBrowserApplet& frontend;
39 52
40 bool complete = false; 53 bool complete = false;
@@ -44,8 +57,16 @@ private:
44 ShimKind kind; 57 ShimKind kind;
45 std::map<WebArgTLVType, std::vector<u8>> args; 58 std::map<WebArgTLVType, std::vector<u8>> args;
46 59
60 FileSys::VirtualFile offline_romfs;
47 std::string temporary_dir; 61 std::string temporary_dir;
48 std::string filename; 62 std::string filename;
63
64 ShopWebTarget shop_web_target;
65 std::map<std::string, std::string> shop_query;
66 std::optional<u64> title_id = 0;
67 std::optional<u128> user_id;
68 std::optional<bool> shop_full_display;
69 std::string shop_extra_parameter;
49}; 70};
50 71
51} // namespace Service::AM::Applets 72} // namespace Service::AM::Applets