libNet: finish NpCommerce dialogs at open (#1166)

* libNet: finish NpCommerce dialogs at open

* libNet: complete NpCommerce dialog cancellation

---------

Co-authored-by: nmzik <Nmzik@mail.ru>
This commit is contained in:
NeySlimandnmzik authored and GitHub committed 2026-10-08 19:31:47 +04:00
1 parent f40755173f
commit 107d5f750a
2 files changed
+163 -3

No files matched your search

+73 -3
View File
@@ -1497,13 +1497,43 @@ namespace LibNpCommerce {
LIB_VERSION("NpCommerce", 1, "NpCommerce", 1, 1);
constexpr int COMMERCE_STATUS_NONE = 0;
constexpr int COMMERCE_STATUS_INITIALIZED = 1;
constexpr int COMMERCE_STATUS_NONE = 0;
constexpr int COMMERCE_STATUS_INITIALIZED = 1;
constexpr int COMMERCE_STATUS_FINISHED = 3;
constexpr int COMMERCE_RESULT_USER_CANCELED = 1;
constexpr int COMMERCE_ERROR_NOT_INITIALIZED = static_cast<int>(0x80B80003u);
constexpr int COMMERCE_ERROR_ALREADY_INITIALIZED = static_cast<int>(0x80B80004u);
constexpr int COMMERCE_ERROR_NOT_FINISHED = static_cast<int>(0x80B80005u);
constexpr int COMMERCE_ERROR_PARAM_INVALID = static_cast<int>(0x80B8000Au);
constexpr int COMMERCE_ERROR_ARG_NULL = static_cast<int>(0x80B8000Du);
static int g_commerce_status = COMMERCE_STATUS_NONE;
struct CommerceDialogParam {
uint8_t base_param[48];
int32_t size;
int32_t user_id;
int32_t mode;
uint32_t service_label;
const char* const* targets;
uint32_t num_targets;
uint32_t padding;
uint64_t features;
void* user_data;
uint8_t reserved[32];
};
struct CommerceDialogResult {
int32_t result;
bool authorized;
void* user_data;
uint8_t reserved[32];
};
static_assert(sizeof(CommerceDialogParam) == 128 && offsetof(CommerceDialogParam, user_data) == 88);
static_assert(sizeof(CommerceDialogResult) == 48 && offsetof(CommerceDialogResult, user_data) == 8);
static int g_commerce_status = COMMERCE_STATUS_NONE;
static CommerceDialogResult g_commerce_result {};
static int KYTY_SYSV_ABI NpCommerceDialogInitialize() {
PRINT_NAME();
@@ -1520,9 +1550,47 @@ static int KYTY_SYSV_ABI NpCommerceDialogTerminate() {
return COMMERCE_ERROR_NOT_INITIALIZED;
}
g_commerce_status = COMMERCE_STATUS_NONE;
g_commerce_result = {};
return OK;
}
static int KYTY_SYSV_ABI NpCommerceDialogOpen(const CommerceDialogParam* param) {
PRINT_NAME();
if (g_commerce_status == COMMERCE_STATUS_NONE) {
return COMMERCE_ERROR_NOT_INITIALIZED;
}
if (param == nullptr) {
return COMMERCE_ERROR_ARG_NULL;
}
g_commerce_result = {};
if (param->size == sizeof(CommerceDialogParam)) {
g_commerce_result.user_data = param->user_data;
}
g_commerce_status = COMMERCE_STATUS_FINISHED;
if (param->size != sizeof(CommerceDialogParam) || param->mode < 0 || param->mode > 5 ||
std::ranges::any_of(param->reserved, [](uint8_t value) { return value != 0; })) {
g_commerce_result.result = COMMERCE_ERROR_PARAM_INVALID;
return COMMERCE_ERROR_PARAM_INVALID;
}
g_commerce_result.result = COMMERCE_RESULT_USER_CANCELED;
return OK;
}
static int KYTY_SYSV_ABI NpCommerceDialogGetResult(CommerceDialogResult* result) {
PRINT_NAME();
if (g_commerce_status == COMMERCE_STATUS_NONE) {
return COMMERCE_ERROR_NOT_INITIALIZED;
}
if (result == nullptr) {
return COMMERCE_ERROR_ARG_NULL;
}
if (g_commerce_status != COMMERCE_STATUS_FINISHED) {
return COMMERCE_ERROR_NOT_FINISHED;
}
*result = g_commerce_result;
return result->result;
}
static int KYTY_SYSV_ABI NpCommerceDialogUpdateStatus() {
PRINT_NAME();
@@ -1532,6 +1600,8 @@ static int KYTY_SYSV_ABI NpCommerceDialogUpdateStatus() {
LIB_DEFINE(InitNet_1_NpCommerce) {
LIB_FUNC("0aR2aWmQal4", NpCommerceDialogInitialize);
LIB_FUNC("m-I92Ab50W8", NpCommerceDialogTerminate);
LIB_FUNC("DfSCDRA3EjY", NpCommerceDialogOpen);
LIB_FUNC("r42bWcQbtZY", NpCommerceDialogGetResult);
LIB_FUNC("LR5cwFMMCVE", NpCommerceDialogUpdateStatus);
}
+90
View File
@@ -62,6 +62,10 @@ namespace Libs::LibNpWebApi2 {
void InitNet_1_NpWebApi2(Loader::SymbolDatabase *symbols);
}
namespace Libs::LibNpCommerce {
void InitNet_1_NpCommerce(Loader::SymbolDatabase *symbols);
}
namespace {
namespace FileSystem = Libs::LibKernel::FileSystem;
@@ -385,6 +389,91 @@ void TestAioBatches() {
Check(FileSystem::KernelClose(fd) == OK, "close AIO read fixture");
}
void TestNpCommerceDialog() {
Loader::SymbolDatabase symbols;
Libs::LibNpCommerce::InitNet_1_NpCommerce(&symbols);
const auto find = [&](const char *nid) {
const auto *symbol = symbols.FindByNid(nid, Loader::SymbolType::Func);
Check(symbol != nullptr, "NpCommerce dialog exports resolve");
return symbol->vaddr;
};
using Call = int (KYTY_SYSV_ABI *)();
using Open = int (KYTY_SYSV_ABI *)(const void *);
using GetResult = int (KYTY_SYSV_ABI *)(void *);
const auto initialize = reinterpret_cast<Call>(find("0aR2aWmQal4"));
const auto terminate = reinterpret_cast<Call>(find("m-I92Ab50W8"));
const auto update = reinterpret_cast<Call>(find("LR5cwFMMCVE"));
const auto open = reinterpret_cast<Open>(find("DfSCDRA3EjY"));
const auto get_result = reinterpret_cast<GetResult>(find("r42bWcQbtZY"));
struct BaseParam {
uint64_t size;
uint8_t reserved[36];
uint32_t magic;
};
struct Param {
BaseParam base;
int32_t size, user_id, mode;
uint32_t service_label;
const char *const *targets;
uint32_t num_targets, padding;
uint64_t features;
void *user_data;
uint8_t reserved[32];
} param {};
struct Result {
int32_t result;
bool authorized;
void *user_data;
uint8_t reserved[32];
} result {};
static_assert(sizeof(Param) == 128 && offsetof(Param, user_data) == 88);
static_assert(sizeof(Result) == 48 && offsetof(Result, user_data) == 8);
param.base.size = sizeof(BaseParam);
param.base.magic = static_cast<uint32_t>(0xC0D1A109u + reinterpret_cast<uintptr_t>(&param.base));
param.size = sizeof(param);
param.user_id = 1;
param.user_data = &symbols;
Check(open(&param) == static_cast<int>(0x80B80003u) &&
get_result(&result) == static_cast<int>(0x80B80003u),
"NpCommerce dialog calls require initialization");
Check(initialize() == OK && update() == 1 &&
get_result(&result) == static_cast<int>(0x80B80005u),
"NpCommerce dialog result requires completion");
Check(open(nullptr) == static_cast<int>(0x80B8000Du) && update() == 1 &&
get_result(nullptr) == static_cast<int>(0x80B8000Du),
"NpCommerce dialog rejects null arguments");
Check(open(&param) == OK && update() == 3, "NpCommerce dialog finishes without a store");
param.user_data = nullptr;
result.authorized = true;
Check(get_result(&result) == 1 && result.result == 1 && !result.authorized &&
result.user_data == &symbols,
"NpCommerce cancellation returns the original user data");
param.mode = 5;
Check(open(&param) == OK && get_result(&result) == 1 && !result.authorized &&
result.user_data == nullptr,
"NpCommerce dialog can reopen with fresh result data");
const auto invalid_param = [&] {
Check(open(&param) == static_cast<int>(0x80B8000Au) && update() == 3 &&
get_result(&result) == static_cast<int>(0x80B8000Au) &&
result.result == static_cast<int>(0x80B8000Au) && !result.authorized,
"NpCommerce dialog reports invalid parameters in the completion result");
};
param.mode = 99;
invalid_param();
param.mode = -1;
invalid_param();
param.mode = 0;
param.size = 64;
invalid_param();
param.size = sizeof(param);
param.reserved[0] = 1;
invalid_param();
param.reserved[0] = 0;
Check(open(&param) == OK && get_result(&result) == 1 && terminate() == OK && update() == 0 &&
get_result(&result) == static_cast<int>(0x80B80003u),
"NpCommerce dialog recovers from invalid parameters and terminates");
}
void TestNpWebApi2Memory() {
Loader::SymbolDatabase symbols;
Libs::LibNpWebApi2::InitNet_1_NpWebApi2(&symbols);
@@ -1644,6 +1733,7 @@ int main(int, char**) {
CheckSocketWakeup();
CheckEtherAddressFormatting();
TestNpWebApi2Memory();
TestNpCommerceDialog();
graphics.reset();
subsystems.Destroy();