Compare commits
10 Commits
toa
...
stop-it-ge
Author | SHA1 | Date | |
---|---|---|---|
17620f3758 | |||
7a4854c519 | |||
d1e3dddf6a | |||
265e8193b9 | |||
e8c20fa782 | |||
95ae46f6a8 | |||
41fe75acb7 | |||
1744537d85 | |||
bea863efff | |||
89e13a85a7 |
6235
dist/languages/fi_FI.ts
vendored
6235
dist/languages/fi_FI.ts
vendored
File diff suppressed because it is too large
Load Diff
@ -687,8 +687,8 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||||||
1,
|
1,
|
||||||
10,
|
10,
|
||||||
"x",
|
"x",
|
||||||
IntSetting.GRAPHICS_API.key,
|
IntSetting.RESOLUTION_FACTOR.key,
|
||||||
IntSetting.GRAPHICS_API.defaultValue.toFloat()
|
IntSetting.RESOLUTION_FACTOR.defaultValue.toFloat()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
add(
|
add(
|
||||||
|
@ -31,7 +31,8 @@ void ConfigureUi::InitializeLanguageComboBox() {
|
|||||||
locale.truncate(locale.lastIndexOf(QLatin1Char{'.'}));
|
locale.truncate(locale.lastIndexOf(QLatin1Char{'.'}));
|
||||||
locale.remove(0, locale.lastIndexOf(QLatin1Char{'/'}) + 1);
|
locale.remove(0, locale.lastIndexOf(QLatin1Char{'/'}) + 1);
|
||||||
const QString lang = QLocale::languageToString(QLocale(locale).language());
|
const QString lang = QLocale::languageToString(QLocale(locale).language());
|
||||||
ui->language_combobox->addItem(lang, locale);
|
const QString country = QLocale::territoryToString(QLocale(locale).territory());
|
||||||
|
ui->language_combobox->addItem(QStringLiteral("%1 (%2)").arg(lang, country), locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlike other configuration changes, interface language changes need to be reflected on the
|
// Unlike other configuration changes, interface language changes need to be reflected on the
|
||||||
|
@ -115,6 +115,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
|||||||
SUB(Service, Y2R) \
|
SUB(Service, Y2R) \
|
||||||
SUB(Service, PS) \
|
SUB(Service, PS) \
|
||||||
SUB(Service, PLGLDR) \
|
SUB(Service, PLGLDR) \
|
||||||
|
SUB(Service, NEWS) \
|
||||||
CLS(HW) \
|
CLS(HW) \
|
||||||
SUB(HW, Memory) \
|
SUB(HW, Memory) \
|
||||||
SUB(HW, LCD) \
|
SUB(HW, LCD) \
|
||||||
|
@ -82,6 +82,7 @@ enum class Class : u8 {
|
|||||||
Service_Y2R, ///< The Y2R (YUV to RGB conversion) service
|
Service_Y2R, ///< The Y2R (YUV to RGB conversion) service
|
||||||
Service_PS, ///< The PS (Process) service
|
Service_PS, ///< The PS (Process) service
|
||||||
Service_PLGLDR, ///< The PLGLDR (plugin loader) service
|
Service_PLGLDR, ///< The PLGLDR (plugin loader) service
|
||||||
|
Service_NEWS, ///< The NEWS (Notifications) service
|
||||||
HW, ///< Low-level hardware emulation
|
HW, ///< Low-level hardware emulation
|
||||||
HW_Memory, ///< Memory-map and address translation
|
HW_Memory, ///< Memory-map and address translation
|
||||||
HW_LCD, ///< LCD register emulation
|
HW_LCD, ///< LCD register emulation
|
||||||
|
@ -101,7 +101,7 @@ void vfp_put_float(ARMul_State* state, s32 val, unsigned int reg) {
|
|||||||
|
|
||||||
u64 vfp_get_double(ARMul_State* state, unsigned int reg) {
|
u64 vfp_get_double(ARMul_State* state, unsigned int reg) {
|
||||||
u64 result = ((u64)state->ExtReg[reg * 2 + 1]) << 32 | state->ExtReg[reg * 2];
|
u64 result = ((u64)state->ExtReg[reg * 2 + 1]) << 32 | state->ExtReg[reg * 2];
|
||||||
LOG_TRACE(Core_ARM11, "VFP get double: s[{}-{}]=[{:016llx}]", reg * 2 + 1, reg * 2, result);
|
LOG_TRACE(Core_ARM11, "VFP get double: s[{}-{}]=[{:016x}]", reg * 2 + 1, reg * 2, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ static struct vfp_double vfp_double_default_qnan = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static void vfp_double_dump(const char* str, struct vfp_double* d) {
|
static void vfp_double_dump(const char* str, struct vfp_double* d) {
|
||||||
LOG_TRACE(Core_ARM11, "VFP: {}: sign={} exponent={} significand={:016llx}", str, d->sign != 0,
|
LOG_TRACE(Core_ARM11, "VFP: {}: sign={} exponent={} significand={:016x}", str, d->sign != 0,
|
||||||
d->exponent, d->significand);
|
d->exponent, d->significand);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,7 +166,7 @@ u32 vfp_double_normaliseround(ARMul_State* state, int dd, struct vfp_double* vd,
|
|||||||
} else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vd->sign != 0))
|
} else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vd->sign != 0))
|
||||||
incr = (1ULL << (VFP_DOUBLE_LOW_BITS + 1)) - 1;
|
incr = (1ULL << (VFP_DOUBLE_LOW_BITS + 1)) - 1;
|
||||||
|
|
||||||
LOG_TRACE(Core_ARM11, "VFP: rounding increment = 0x{:08llx}", incr);
|
LOG_TRACE(Core_ARM11, "VFP: rounding increment = 0x{:08x}", incr);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Is our rounding going to overflow?
|
* Is our rounding going to overflow?
|
||||||
@ -221,8 +221,7 @@ pack:
|
|||||||
vfp_double_dump("pack: final", vd);
|
vfp_double_dump("pack: final", vd);
|
||||||
{
|
{
|
||||||
s64 d = vfp_double_pack(vd);
|
s64 d = vfp_double_pack(vd);
|
||||||
LOG_TRACE(Core_ARM11, "VFP: {}: d(d{})={:016llx} exceptions={:08x}", func, dd, d,
|
LOG_TRACE(Core_ARM11, "VFP: {}: d(d{})={:016x} exceptions={:08x}", func, dd, d, exceptions);
|
||||||
exceptions);
|
|
||||||
vfp_put_double(state, d, dd);
|
vfp_put_double(state, d, dd);
|
||||||
}
|
}
|
||||||
return exceptions;
|
return exceptions;
|
||||||
|
@ -2,18 +2,765 @@
|
|||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include "common/archives.h"
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/file_util.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "common/scope_exit.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
#include "core/file_sys/archive_systemsavedata.h"
|
||||||
|
#include "core/file_sys/errors.h"
|
||||||
|
#include "core/file_sys/file_backend.h"
|
||||||
|
#include "core/hle/ipc_helpers.h"
|
||||||
|
#include "core/hle/kernel/shared_page.h"
|
||||||
|
#include "core/hle/result.h"
|
||||||
|
#include "core/hle/service/fs/fs_user.h"
|
||||||
|
#include "core/hle/service/news/news.h"
|
||||||
#include "core/hle/service/news/news_s.h"
|
#include "core/hle/service/news/news_s.h"
|
||||||
#include "core/hle/service/news/news_u.h"
|
#include "core/hle/service/news/news_u.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
|
SERVICE_CONSTRUCT_IMPL(Service::NEWS::Module)
|
||||||
|
|
||||||
namespace Service::NEWS {
|
namespace Service::NEWS {
|
||||||
|
|
||||||
|
namespace ErrCodes {
|
||||||
|
enum {
|
||||||
|
/// This error is returned if either the NewsDB header or the header for a notification ID is
|
||||||
|
/// invalid
|
||||||
|
InvalidHeader = 5,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Result ErrorInvalidHeader = // 0xC8A12805
|
||||||
|
Result(ErrCodes::InvalidHeader, ErrorModule::News, ErrorSummary::InvalidState,
|
||||||
|
ErrorLevel::Status);
|
||||||
|
|
||||||
|
constexpr std::array<u8, 8> news_system_savedata_id{
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x01, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class Archive>
|
||||||
|
void Module::serialize(Archive& ar, const unsigned int) {
|
||||||
|
ar& db;
|
||||||
|
ar& notification_ids;
|
||||||
|
ar& automatic_sync_flag;
|
||||||
|
ar& news_system_save_data_archive;
|
||||||
|
}
|
||||||
|
SERIALIZE_IMPL(Module)
|
||||||
|
|
||||||
|
void Module::Interface::AddNotificationImpl(Kernel::HLERequestContext& ctx, bool news_s) {
|
||||||
|
IPC::RequestParser rp(ctx);
|
||||||
|
const u32 header_size = rp.Pop<u32>();
|
||||||
|
const u32 message_size = rp.Pop<u32>();
|
||||||
|
const u32 image_size = rp.Pop<u32>();
|
||||||
|
|
||||||
|
u32 process_id;
|
||||||
|
if (!news_s) {
|
||||||
|
process_id = rp.PopPID();
|
||||||
|
LOG_INFO(Service_NEWS,
|
||||||
|
"called header_size=0x{:x}, message_size=0x{:x}, image_size=0x{:x}, process_id={}",
|
||||||
|
header_size, message_size, image_size, process_id);
|
||||||
|
} else {
|
||||||
|
LOG_INFO(Service_NEWS, "called header_size=0x{:x}, message_size=0x{:x}, image_size=0x{:x}",
|
||||||
|
header_size, message_size, image_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto header_buffer = rp.PopMappedBuffer();
|
||||||
|
auto message_buffer = rp.PopMappedBuffer();
|
||||||
|
auto image_buffer = rp.PopMappedBuffer();
|
||||||
|
|
||||||
|
NotificationHeader header{};
|
||||||
|
header_buffer.Read(&header, 0,
|
||||||
|
std::min(sizeof(NotificationHeader), static_cast<std::size_t>(header_size)));
|
||||||
|
|
||||||
|
std::vector<u8> message(message_size);
|
||||||
|
message_buffer.Read(message.data(), 0, message.size());
|
||||||
|
|
||||||
|
std::vector<u8> image(image_size);
|
||||||
|
image_buffer.Read(image.data(), 0, image.size());
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 6);
|
||||||
|
SCOPE_EXIT({
|
||||||
|
rb.PushMappedBuffer(header_buffer);
|
||||||
|
rb.PushMappedBuffer(message_buffer);
|
||||||
|
rb.PushMappedBuffer(image_buffer);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!news_s) {
|
||||||
|
// Set the program_id using the input process ID
|
||||||
|
auto fs_user = news->system.ServiceManager().GetService<Service::FS::FS_USER>("fs:USER");
|
||||||
|
ASSERT_MSG(fs_user != nullptr, "fs:USER service is missing.");
|
||||||
|
|
||||||
|
auto program_info_result = fs_user->GetProgramLaunchInfo(process_id);
|
||||||
|
if (program_info_result.Failed()) {
|
||||||
|
rb.Push(program_info_result.Code());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.program_id = program_info_result.Unwrap().program_id;
|
||||||
|
|
||||||
|
// The date_time is set by the sysmodule on news:u requests
|
||||||
|
auto& share_page = news->system.Kernel().GetSharedPageHandler();
|
||||||
|
header.date_time = share_page.GetSystemTimeSince2000();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto save_result = news->SaveNotification(&header, header_size, message, image);
|
||||||
|
if (R_FAILED(save_result)) {
|
||||||
|
rb.Push(save_result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the DB header new notification flag
|
||||||
|
if ((news->db.header.flags & 1) == 0) {
|
||||||
|
news->db.header.flags |= 1;
|
||||||
|
const auto db_result = news->SaveNewsDBSavedata();
|
||||||
|
if (R_FAILED(db_result)) {
|
||||||
|
rb.Push(db_result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::Interface::AddNotification(Kernel::HLERequestContext& ctx) {
|
||||||
|
AddNotificationImpl(ctx, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::Interface::AddNotificationSystem(Kernel::HLERequestContext& ctx) {
|
||||||
|
AddNotificationImpl(ctx, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::Interface::ResetNotifications(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx);
|
||||||
|
|
||||||
|
LOG_INFO(Service_NEWS, "called");
|
||||||
|
|
||||||
|
// Cleanup the sorted notification IDs
|
||||||
|
for (u32 i = 0; i < MAX_NOTIFICATIONS; ++i) {
|
||||||
|
news->notification_ids[i] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
|
||||||
|
FileSys::ArchiveFactory_SystemSaveData systemsavedata_factory(nand_directory);
|
||||||
|
|
||||||
|
FileSys::Path archive_path(news_system_savedata_id);
|
||||||
|
|
||||||
|
// Format the SystemSaveData archive 0x00010035
|
||||||
|
systemsavedata_factory.Format(archive_path, FileSys::ArchiveFormatInfo(), 0);
|
||||||
|
|
||||||
|
news->news_system_save_data_archive = systemsavedata_factory.Open(archive_path, 0).Unwrap();
|
||||||
|
|
||||||
|
// NOTE: The original sysmodule doesn't clear the News DB in memory
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::Interface::GetTotalNotifications(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx);
|
||||||
|
|
||||||
|
LOG_INFO(Service_NEWS, "called");
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.Push(static_cast<u32>(news->GetTotalNotifications()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::Interface::SetNewsDBHeader(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx);
|
||||||
|
const u32 size = rp.Pop<u32>();
|
||||||
|
auto input_buffer = rp.PopMappedBuffer();
|
||||||
|
|
||||||
|
LOG_INFO(Service_NEWS, "called size=0x{:x}", size);
|
||||||
|
|
||||||
|
NewsDBHeader header{};
|
||||||
|
input_buffer.Read(&header, 0, std::min(sizeof(NewsDBHeader), static_cast<std::size_t>(size)));
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||||
|
rb.Push(news->SetNewsDBHeader(&header, size));
|
||||||
|
rb.PushMappedBuffer(input_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::Interface::SetNotificationHeader(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx);
|
||||||
|
const u32 notification_index = rp.Pop<u32>();
|
||||||
|
const u32 size = rp.Pop<u32>();
|
||||||
|
auto input_buffer = rp.PopMappedBuffer();
|
||||||
|
|
||||||
|
LOG_INFO(Service_NEWS, "called notification_index={}, size=0x{:x}", notification_index, size);
|
||||||
|
|
||||||
|
NotificationHeader header{};
|
||||||
|
input_buffer.Read(&header, 0,
|
||||||
|
std::min(sizeof(NotificationHeader), static_cast<std::size_t>(size)));
|
||||||
|
|
||||||
|
const auto result = news->SetNotificationHeader(notification_index, &header, size);
|
||||||
|
|
||||||
|
// TODO(DaniElectra): If flag_boss == 1, the original sysmodule updates the optout status of the
|
||||||
|
// source program on SpotPass with the boss:P command 0x00040600C0 (possibly named
|
||||||
|
// SetOptoutFlagPrivileged?) using the program_id and flag_optout as parameter
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||||
|
rb.Push(result);
|
||||||
|
rb.PushMappedBuffer(input_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::Interface::SetNotificationMessage(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx);
|
||||||
|
const u32 notification_index = rp.Pop<u32>();
|
||||||
|
const u32 size = rp.Pop<u32>();
|
||||||
|
auto input_buffer = rp.PopMappedBuffer();
|
||||||
|
|
||||||
|
LOG_INFO(Service_NEWS, "called notification_index={}, size=0x{:x}", notification_index, size);
|
||||||
|
|
||||||
|
std::vector<u8> data(size);
|
||||||
|
input_buffer.Read(data.data(), 0, data.size());
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||||
|
rb.Push(news->SetNotificationMessage(notification_index, data));
|
||||||
|
rb.PushMappedBuffer(input_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::Interface::SetNotificationImage(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx);
|
||||||
|
const u32 notification_index = rp.Pop<u32>();
|
||||||
|
const u32 size = rp.Pop<u32>();
|
||||||
|
auto input_buffer = rp.PopMappedBuffer();
|
||||||
|
|
||||||
|
LOG_INFO(Service_NEWS, "called notification_index={}, size=0x{:x}", notification_index, size);
|
||||||
|
|
||||||
|
std::vector<u8> data(size);
|
||||||
|
input_buffer.Read(data.data(), 0, data.size());
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||||
|
rb.Push(news->SetNotificationImage(notification_index, data));
|
||||||
|
rb.PushMappedBuffer(input_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::Interface::GetNewsDBHeader(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx);
|
||||||
|
const u32 size = rp.Pop<u32>();
|
||||||
|
auto output_buffer = rp.PopMappedBuffer();
|
||||||
|
|
||||||
|
LOG_INFO(Service_NEWS, "called size=0x{:x}", size);
|
||||||
|
|
||||||
|
NewsDBHeader header{};
|
||||||
|
const auto result = news->GetNewsDBHeader(&header, size);
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
|
||||||
|
|
||||||
|
if (result.Failed()) {
|
||||||
|
rb.Push(result.Code());
|
||||||
|
rb.Push<u32>(0);
|
||||||
|
} else {
|
||||||
|
const auto copied_size = result.Unwrap();
|
||||||
|
output_buffer.Write(&header, 0, copied_size);
|
||||||
|
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.Push<u32>(static_cast<u32>(copied_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
rb.PushMappedBuffer(output_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::Interface::GetNotificationHeader(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx);
|
||||||
|
const u32 notification_index = rp.Pop<u32>();
|
||||||
|
const u32 size = rp.Pop<u32>();
|
||||||
|
auto output_buffer = rp.PopMappedBuffer();
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_NEWS, "called notification_index={}, size=0x{:x}", notification_index, size);
|
||||||
|
|
||||||
|
NotificationHeader header{};
|
||||||
|
const auto result = news->GetNotificationHeader(notification_index, &header, size);
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
|
||||||
|
SCOPE_EXIT({ rb.PushMappedBuffer(output_buffer); });
|
||||||
|
|
||||||
|
if (result.Failed()) {
|
||||||
|
rb.Push(result.Code());
|
||||||
|
rb.Push<u32>(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(DaniElectra): If flag_boss == 1, the original sysmodule updates the optout flag of the
|
||||||
|
// header with the result of boss:P command 0x0004070080 (possibly named
|
||||||
|
// GetOptoutFlagPrivileged?) using the program_id as parameter
|
||||||
|
|
||||||
|
const auto copied_size = result.Unwrap();
|
||||||
|
output_buffer.Write(&header, 0, copied_size);
|
||||||
|
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.Push<u32>(static_cast<u32>(copied_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::Interface::GetNotificationMessage(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx);
|
||||||
|
const u32 notification_index = rp.Pop<u32>();
|
||||||
|
const u32 size = rp.Pop<u32>();
|
||||||
|
auto output_buffer = rp.PopMappedBuffer();
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_NEWS, "called notification_index={}, size=0x{:x}", notification_index, size);
|
||||||
|
|
||||||
|
std::vector<u8> message(size);
|
||||||
|
const auto result = news->GetNotificationMessage(notification_index, message);
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
|
||||||
|
SCOPE_EXIT({ rb.PushMappedBuffer(output_buffer); });
|
||||||
|
|
||||||
|
if (result.Failed()) {
|
||||||
|
rb.Push(result.Code());
|
||||||
|
rb.Push<u32>(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto copied_size = result.Unwrap();
|
||||||
|
output_buffer.Write(message.data(), 0, copied_size);
|
||||||
|
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.Push<u32>(static_cast<u32>(copied_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::Interface::GetNotificationImage(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx);
|
||||||
|
const u32 notification_index = rp.Pop<u32>();
|
||||||
|
const u32 size = rp.Pop<u32>();
|
||||||
|
auto output_buffer = rp.PopMappedBuffer();
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_NEWS, "called notification_index={}, size=0x{:x}", notification_index, size);
|
||||||
|
|
||||||
|
std::vector<u8> image(size);
|
||||||
|
const auto result = news->GetNotificationImage(notification_index, image);
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
|
||||||
|
SCOPE_EXIT({ rb.PushMappedBuffer(output_buffer); });
|
||||||
|
|
||||||
|
if (result.Failed()) {
|
||||||
|
rb.Push(result.Code());
|
||||||
|
rb.Push<u32>(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto copied_size = result.Unwrap();
|
||||||
|
output_buffer.Write(image.data(), 0, copied_size);
|
||||||
|
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.Push<u32>(static_cast<u32>(copied_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::Interface::SetAutomaticSyncFlag(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx);
|
||||||
|
const u8 flag = rp.Pop<u8>();
|
||||||
|
|
||||||
|
LOG_INFO(Service_NEWS, "called flag=0x{:x}", flag);
|
||||||
|
|
||||||
|
news->automatic_sync_flag = flag;
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::Interface::SetNotificationHeaderOther(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx);
|
||||||
|
const u32 notification_index = rp.Pop<u32>();
|
||||||
|
const u32 size = rp.Pop<u32>();
|
||||||
|
auto output_buffer = rp.PopMappedBuffer();
|
||||||
|
|
||||||
|
LOG_INFO(Service_NEWS, "called notification_index={}, size=0x{:x}", notification_index, size);
|
||||||
|
|
||||||
|
NotificationHeader header{};
|
||||||
|
output_buffer.Read(&header, 0,
|
||||||
|
std::min(sizeof(NotificationHeader), static_cast<std::size_t>(size)));
|
||||||
|
|
||||||
|
const auto result = news->SetNotificationHeaderOther(notification_index, &header, size);
|
||||||
|
|
||||||
|
// TODO(DaniElectra): If flag_boss == 1, the original sysmodule updates the optout status of the
|
||||||
|
// source program on SpotPass with the boss:P command 0x00040600C0 (possibly named
|
||||||
|
// SetOptoutFlagPrivileged?) using the program_id and flag_optout as parameter
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||||
|
rb.Push(result);
|
||||||
|
rb.PushMappedBuffer(output_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::Interface::WriteNewsDBSavedata(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx);
|
||||||
|
|
||||||
|
LOG_INFO(Service_NEWS, "called");
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
rb.Push(news->SaveNewsDBSavedata());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::Interface::GetTotalArrivedNotifications(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx);
|
||||||
|
|
||||||
|
LOG_WARNING(Service_NEWS, "(STUBBED) called");
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.Push<u32>(0); // Total number of pending BOSS notifications to be synced
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t Module::GetTotalNotifications() {
|
||||||
|
return std::count_if(
|
||||||
|
notification_ids.begin(), notification_ids.end(),
|
||||||
|
[this](const u32 notification_id) { return db.notifications[notification_id].IsValid(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultVal<std::size_t> Module::GetNewsDBHeader(NewsDBHeader* header, const std::size_t size) {
|
||||||
|
if (!db.header.IsValid()) {
|
||||||
|
return ErrorInvalidHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::size_t copy_size = std::min(sizeof(NewsDBHeader), size);
|
||||||
|
std::memcpy(header, &db.header, copy_size);
|
||||||
|
return copy_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultVal<std::size_t> Module::GetNotificationHeader(const u32 notification_index,
|
||||||
|
NotificationHeader* header,
|
||||||
|
const std::size_t size) {
|
||||||
|
if (!db.header.IsValid()) {
|
||||||
|
return ErrorInvalidHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notification_index >= MAX_NOTIFICATIONS) {
|
||||||
|
return ErrorInvalidHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u32 notification_id = notification_ids[notification_index];
|
||||||
|
if (!db.notifications[notification_id].IsValid()) {
|
||||||
|
return ErrorInvalidHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::size_t copy_size = std::min(sizeof(NotificationHeader), size);
|
||||||
|
std::memcpy(header, &db.notifications[notification_id], copy_size);
|
||||||
|
return copy_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultVal<std::size_t> Module::GetNotificationMessage(const u32 notification_index,
|
||||||
|
std::span<u8> message) {
|
||||||
|
if (!db.header.IsValid()) {
|
||||||
|
return ErrorInvalidHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notification_index >= MAX_NOTIFICATIONS) {
|
||||||
|
return ErrorInvalidHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u32 notification_id = notification_ids[notification_index];
|
||||||
|
if (!db.notifications[notification_id].IsValid()) {
|
||||||
|
return ErrorInvalidHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string message_file = fmt::format("/news{:03d}.txt", notification_id);
|
||||||
|
const auto result = LoadFileFromSavedata(message_file, message);
|
||||||
|
if (result.Failed()) {
|
||||||
|
return result.Code();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.Unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultVal<std::size_t> Module::GetNotificationImage(const u32 notification_index,
|
||||||
|
std::span<u8> image) {
|
||||||
|
if (!db.header.IsValid()) {
|
||||||
|
return ErrorInvalidHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notification_index >= MAX_NOTIFICATIONS) {
|
||||||
|
return ErrorInvalidHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u32 notification_id = notification_ids[notification_index];
|
||||||
|
if (!db.notifications[notification_id].IsValid()) {
|
||||||
|
return ErrorInvalidHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string image_file = fmt::format("/news{:03d}.mpo", notification_id);
|
||||||
|
const auto result = LoadFileFromSavedata(image_file, image);
|
||||||
|
if (result.Failed()) {
|
||||||
|
return result.Code();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.Unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Module::SetNewsDBHeader(const NewsDBHeader* header, const std::size_t size) {
|
||||||
|
const std::size_t copy_size = std::min(sizeof(NewsDBHeader), static_cast<std::size_t>(size));
|
||||||
|
std::memcpy(&db.header, header, copy_size);
|
||||||
|
return SaveNewsDBSavedata();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Module::SetNotificationHeader(const u32 notification_index, const NotificationHeader* header,
|
||||||
|
const std::size_t size) {
|
||||||
|
if (notification_index >= MAX_NOTIFICATIONS) {
|
||||||
|
return ErrorInvalidHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u32 notification_id = notification_ids[notification_index];
|
||||||
|
const std::size_t copy_size = std::min(sizeof(NotificationHeader), size);
|
||||||
|
std::memcpy(&db.notifications[notification_id], header, copy_size);
|
||||||
|
return SaveNewsDBSavedata();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Module::SetNotificationHeaderOther(const u32 notification_index,
|
||||||
|
const NotificationHeader* header,
|
||||||
|
const std::size_t size) {
|
||||||
|
if (notification_index >= MAX_NOTIFICATIONS) {
|
||||||
|
return ErrorInvalidHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u32 notification_id = notification_ids[notification_index];
|
||||||
|
const std::size_t copy_size = std::min(sizeof(NotificationHeader), size);
|
||||||
|
std::memcpy(&db.notifications[notification_id], header, copy_size);
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Module::SetNotificationMessage(const u32 notification_index, std::span<const u8> message) {
|
||||||
|
if (notification_index >= MAX_NOTIFICATIONS) {
|
||||||
|
return ErrorInvalidHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u32 notification_id = notification_ids[notification_index];
|
||||||
|
const std::string message_file = fmt::format("/news{:03d}.txt", notification_id);
|
||||||
|
return SaveFileToSavedata(message_file, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Module::SetNotificationImage(const u32 notification_index, std::span<const u8> image) {
|
||||||
|
if (notification_index >= MAX_NOTIFICATIONS) {
|
||||||
|
return ErrorInvalidHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u32 notification_id = notification_ids[notification_index];
|
||||||
|
const std::string image_file = fmt::format("/news{:03d}.mpo", notification_id);
|
||||||
|
return SaveFileToSavedata(image_file, image);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Module::SaveNotification(const NotificationHeader* header, const std::size_t header_size,
|
||||||
|
std::span<const u8> message, std::span<const u8> image) {
|
||||||
|
if (!db.header.IsValid()) {
|
||||||
|
return ErrorInvalidHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!header->IsValid()) {
|
||||||
|
return ErrorInvalidHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 notification_count = static_cast<u32>(GetTotalNotifications());
|
||||||
|
|
||||||
|
// If we have reached the limit of 100 notifications, delete the oldest one
|
||||||
|
if (notification_count >= MAX_NOTIFICATIONS) {
|
||||||
|
LOG_WARNING(Service_NEWS,
|
||||||
|
"Notification limit has been reached. Deleting oldest notification ID: {}",
|
||||||
|
notification_ids[0]);
|
||||||
|
R_TRY(DeleteNotification(notification_ids[0]));
|
||||||
|
|
||||||
|
notification_count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if there is enough space for storing the new notification data. The header is already
|
||||||
|
// allocated with the News DB
|
||||||
|
const u64 needed_space = static_cast<u64>(message.size() + image.size());
|
||||||
|
while (notification_count > 0) {
|
||||||
|
const u64 free_space = news_system_save_data_archive->GetFreeBytes();
|
||||||
|
if (needed_space <= free_space) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_WARNING(Service_NEWS, "Not enough space available. Deleting oldest notification ID: {}",
|
||||||
|
notification_ids[0]);
|
||||||
|
|
||||||
|
// If we don't have space, delete old notifications until we do
|
||||||
|
R_TRY(DeleteNotification(notification_ids[0]));
|
||||||
|
|
||||||
|
notification_count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_NEWS, "New notification: notification_id={}, title={}",
|
||||||
|
notification_ids[notification_count], Common::UTF16BufferToUTF8(header->title));
|
||||||
|
|
||||||
|
if (!image.empty()) {
|
||||||
|
R_TRY(SetNotificationImage(notification_count, image));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!message.empty()) {
|
||||||
|
R_TRY(SetNotificationMessage(notification_count, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
R_TRY(SetNotificationHeader(notification_count, header, header_size));
|
||||||
|
|
||||||
|
// Sort the notifications after saving
|
||||||
|
std::sort(notification_ids.begin(), notification_ids.end(),
|
||||||
|
[this](const u32 first_id, const u32 second_id) -> bool {
|
||||||
|
return CompareNotifications(first_id, second_id);
|
||||||
|
});
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Module::DeleteNotification(const u32 notification_id) {
|
||||||
|
bool deleted = false;
|
||||||
|
|
||||||
|
// Check if the input notification ID exists, and clear it
|
||||||
|
if (db.notifications[notification_id].IsValid()) {
|
||||||
|
db.notifications[notification_id] = {};
|
||||||
|
|
||||||
|
R_TRY(SaveNewsDBSavedata());
|
||||||
|
|
||||||
|
deleted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup images and messages for invalid notifications
|
||||||
|
for (u32 i = 0; i < MAX_NOTIFICATIONS; ++i) {
|
||||||
|
if (!db.notifications[i].IsValid()) {
|
||||||
|
const std::string image_file = fmt::format("/news{:03d}.mpo", i);
|
||||||
|
auto result = news_system_save_data_archive->DeleteFile(image_file);
|
||||||
|
if (R_FAILED(result) && result != FileSys::ResultFileNotFound) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string message_file = fmt::format("/news{:03d}.txt", i);
|
||||||
|
result = news_system_save_data_archive->DeleteFile(message_file);
|
||||||
|
if (R_FAILED(result) && result != FileSys::ResultFileNotFound) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the input notification ID was deleted, reorder the notification IDs list
|
||||||
|
if (deleted) {
|
||||||
|
std::sort(notification_ids.begin(), notification_ids.end(),
|
||||||
|
[this](const u32 first_id, const u32 second_id) -> bool {
|
||||||
|
return CompareNotifications(first_id, second_id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Module::LoadNewsDBSavedata() {
|
||||||
|
const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
|
||||||
|
FileSys::ArchiveFactory_SystemSaveData systemsavedata_factory(nand_directory);
|
||||||
|
|
||||||
|
// Open the SystemSaveData archive 0x00010035
|
||||||
|
FileSys::Path archive_path(news_system_savedata_id);
|
||||||
|
auto archive_result = systemsavedata_factory.Open(archive_path, 0);
|
||||||
|
|
||||||
|
// If the archive didn't exist, create the files inside
|
||||||
|
if (archive_result.Code() == FileSys::ResultNotFound) {
|
||||||
|
// Format the archive to create the directories
|
||||||
|
systemsavedata_factory.Format(archive_path, FileSys::ArchiveFormatInfo(), 0);
|
||||||
|
|
||||||
|
// Open it again to get a valid archive now that the folder exists
|
||||||
|
news_system_save_data_archive = systemsavedata_factory.Open(archive_path, 0).Unwrap();
|
||||||
|
} else {
|
||||||
|
ASSERT_MSG(archive_result.Succeeded(), "Could not open the NEWS SystemSaveData archive!");
|
||||||
|
|
||||||
|
news_system_save_data_archive = std::move(archive_result).Unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string news_db_file = "/news.db";
|
||||||
|
auto news_result =
|
||||||
|
LoadFileFromSavedata(news_db_file, std::span{reinterpret_cast<u8*>(&db), sizeof(NewsDB)});
|
||||||
|
|
||||||
|
// Read the file if it already exists
|
||||||
|
if (news_result.Failed()) {
|
||||||
|
// Create the file immediately if it doesn't exist
|
||||||
|
db.header = {.valid = 1};
|
||||||
|
news_result = SaveFileToSavedata(
|
||||||
|
news_db_file, std::span{reinterpret_cast<const u8*>(&db), sizeof(NewsDB)});
|
||||||
|
} else {
|
||||||
|
// Sort the notifications from the file
|
||||||
|
std::sort(notification_ids.begin(), notification_ids.end(),
|
||||||
|
[this](const u32 first_id, const u32 second_id) -> bool {
|
||||||
|
return CompareNotifications(first_id, second_id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return news_result.Code();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Module::SaveNewsDBSavedata() {
|
||||||
|
return SaveFileToSavedata("/news.db",
|
||||||
|
std::span{reinterpret_cast<const u8*>(&db), sizeof(NewsDB)});
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultVal<std::size_t> Module::LoadFileFromSavedata(std::string filename, std::span<u8> buffer) {
|
||||||
|
FileSys::Mode mode = {};
|
||||||
|
mode.read_flag.Assign(1);
|
||||||
|
|
||||||
|
FileSys::Path path(filename);
|
||||||
|
|
||||||
|
auto result = news_system_save_data_archive->OpenFile(path, mode);
|
||||||
|
if (result.Failed()) {
|
||||||
|
return result.Code();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto file = std::move(result).Unwrap();
|
||||||
|
const auto bytes_read = file->Read(0, buffer.size(), buffer.data());
|
||||||
|
file->Close();
|
||||||
|
|
||||||
|
ASSERT_MSG(bytes_read.Succeeded(), "could not read file");
|
||||||
|
|
||||||
|
return bytes_read.Unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Module::SaveFileToSavedata(std::string filename, std::span<const u8> buffer) {
|
||||||
|
FileSys::Mode mode = {};
|
||||||
|
mode.write_flag.Assign(1);
|
||||||
|
mode.create_flag.Assign(1);
|
||||||
|
|
||||||
|
FileSys::Path path(filename);
|
||||||
|
|
||||||
|
auto result = news_system_save_data_archive->OpenFile(path, mode);
|
||||||
|
ASSERT_MSG(result.Succeeded(), "could not open file");
|
||||||
|
|
||||||
|
auto file = std::move(result).Unwrap();
|
||||||
|
file->Write(0, buffer.size(), 1, buffer.data());
|
||||||
|
file->Close();
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Module::CompareNotifications(const u32 first_id, const u32 second_id) {
|
||||||
|
// Notification IDs are sorted by date time, with valid notifications being first.
|
||||||
|
// This is done so that other system applications like the News applet can easily
|
||||||
|
// iterate over the notifications with an incrementing index.
|
||||||
|
ASSERT(first_id < MAX_NOTIFICATIONS && second_id < MAX_NOTIFICATIONS);
|
||||||
|
|
||||||
|
if (!db.notifications[first_id].IsValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!db.notifications[second_id].IsValid()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return db.notifications[first_id].date_time < db.notifications[second_id].date_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
Module::Interface::Interface(std::shared_ptr<Module> news, const char* name, u32 max_session)
|
||||||
|
: ServiceFramework(name, max_session), news(std::move(news)) {}
|
||||||
|
|
||||||
|
Module::Module(Core::System& system_) : system(system_) {
|
||||||
|
for (u32 i = 0; i < MAX_NOTIFICATIONS; ++i) {
|
||||||
|
notification_ids[i] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadNewsDBSavedata();
|
||||||
|
}
|
||||||
|
|
||||||
void InstallInterfaces(Core::System& system) {
|
void InstallInterfaces(Core::System& system) {
|
||||||
auto& service_manager = system.ServiceManager();
|
auto& service_manager = system.ServiceManager();
|
||||||
std::make_shared<NEWS_S>()->InstallAsService(service_manager);
|
auto news = std::make_shared<Module>(system);
|
||||||
std::make_shared<NEWS_U>()->InstallAsService(service_manager);
|
std::make_shared<NEWS_S>(news)->InstallAsService(service_manager);
|
||||||
|
std::make_shared<NEWS_U>(news)->InstallAsService(service_manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Service::NEWS
|
} // namespace Service::NEWS
|
||||||
|
@ -4,12 +4,482 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/file_sys/archive_backend.h"
|
||||||
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
class System;
|
class System;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Service::NEWS {
|
namespace Service::NEWS {
|
||||||
|
constexpr u32 MAX_NOTIFICATIONS = 100;
|
||||||
|
|
||||||
|
struct NewsDBHeader {
|
||||||
|
u8 valid;
|
||||||
|
u8 flags;
|
||||||
|
INSERT_PADDING_BYTES(0xE);
|
||||||
|
|
||||||
|
bool IsValid() const {
|
||||||
|
return valid == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <class Archive>
|
||||||
|
void serialize(Archive& ar, const unsigned int) {
|
||||||
|
ar& valid;
|
||||||
|
ar& flags;
|
||||||
|
}
|
||||||
|
friend class boost::serialization::access;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(NewsDBHeader) == 0x10, "News DB Header structure size is wrong");
|
||||||
|
|
||||||
|
struct NotificationHeader {
|
||||||
|
u8 flag_valid;
|
||||||
|
u8 flag_read;
|
||||||
|
u8 flag_jpeg;
|
||||||
|
u8 flag_boss;
|
||||||
|
u8 flag_optout;
|
||||||
|
u8 flag_url;
|
||||||
|
u8 flag_unk0x6;
|
||||||
|
INSERT_PADDING_BYTES(0x1);
|
||||||
|
u64_le program_id;
|
||||||
|
u32_le ns_data_id; // Only used in BOSS notifications
|
||||||
|
u32_le version; // Only used in BOSS notifications
|
||||||
|
u64_le jump_param;
|
||||||
|
INSERT_PADDING_BYTES(0x8);
|
||||||
|
u64_le date_time;
|
||||||
|
std::array<u16_le, 0x20> title;
|
||||||
|
|
||||||
|
bool IsValid() const {
|
||||||
|
return flag_valid == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <class Archive>
|
||||||
|
void serialize(Archive& ar, const unsigned int) {
|
||||||
|
ar& flag_valid;
|
||||||
|
ar& flag_read;
|
||||||
|
ar& flag_jpeg;
|
||||||
|
ar& flag_boss;
|
||||||
|
ar& flag_optout;
|
||||||
|
ar& flag_url;
|
||||||
|
ar& flag_unk0x6;
|
||||||
|
ar& program_id;
|
||||||
|
ar& ns_data_id;
|
||||||
|
ar& version;
|
||||||
|
ar& jump_param;
|
||||||
|
ar& date_time;
|
||||||
|
ar& title;
|
||||||
|
}
|
||||||
|
friend class boost::serialization::access;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(NotificationHeader) == 0x70, "Notification Header structure size is wrong");
|
||||||
|
|
||||||
|
struct NewsDB {
|
||||||
|
NewsDBHeader header;
|
||||||
|
std::array<NotificationHeader, MAX_NOTIFICATIONS> notifications;
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <class Archive>
|
||||||
|
void serialize(Archive& ar, const unsigned int) {
|
||||||
|
ar& header;
|
||||||
|
ar& notifications;
|
||||||
|
}
|
||||||
|
friend class boost::serialization::access;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(NewsDB) == 0x2BD0, "News DB structure size is wrong");
|
||||||
|
|
||||||
|
class Module final {
|
||||||
|
public:
|
||||||
|
explicit Module(Core::System& system_);
|
||||||
|
~Module() = default;
|
||||||
|
|
||||||
|
class Interface : public ServiceFramework<Interface> {
|
||||||
|
public:
|
||||||
|
Interface(std::shared_ptr<Module> news, const char* name, u32 max_session);
|
||||||
|
~Interface() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void AddNotificationImpl(Kernel::HLERequestContext& ctx, bool news_s);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* AddNotification NEWS:U service function.
|
||||||
|
* Inputs:
|
||||||
|
* 0 : 0x000100C8
|
||||||
|
* 1 : Header size
|
||||||
|
* 2 : Message size
|
||||||
|
* 3 : Image size
|
||||||
|
* 4 : PID Translation Header (0x20)
|
||||||
|
* 5 : Caller PID
|
||||||
|
* 6 : Header Buffer Mapping Translation Header ((Size << 4) | 0xA)
|
||||||
|
* 7 : Header Buffer Pointer
|
||||||
|
* 8 : Message Buffer Mapping Translation Header ((Size << 4) | 0xA)
|
||||||
|
* 9 : Message Buffer Pointer
|
||||||
|
* 10 : Image Buffer Mapping Translation Header ((Size << 4) | 0xA)
|
||||||
|
* 11 : Image Buffer Pointer
|
||||||
|
* Outputs:
|
||||||
|
* 0 : 0x00010046
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
void AddNotification(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AddNotification NEWS:S service function.
|
||||||
|
* Inputs:
|
||||||
|
* 0 : 0x000100C6
|
||||||
|
* 1 : Header size
|
||||||
|
* 2 : Message size
|
||||||
|
* 3 : Image size
|
||||||
|
* 4 : Header Buffer Mapping Translation Header ((Size << 4) | 0xA)
|
||||||
|
* 5 : Header Buffer Pointer
|
||||||
|
* 6 : Message Buffer Mapping Translation Header ((Size << 4) | 0xA)
|
||||||
|
* 7 : Message Buffer Pointer
|
||||||
|
* 8 : Image Buffer Mapping Translation Header ((Size << 4) | 0xA)
|
||||||
|
* 9 : Image Buffer Pointer
|
||||||
|
* Outputs:
|
||||||
|
* 0 : 0x00010046
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
void AddNotificationSystem(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ResetNotifications service function.
|
||||||
|
* Inputs:
|
||||||
|
* 0 : 0x00040000
|
||||||
|
* Outputs:
|
||||||
|
* 0 : 0x00040040
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
void ResetNotifications(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetTotalNotifications service function.
|
||||||
|
* Inputs:
|
||||||
|
* 0 : 0x00050000
|
||||||
|
* Outputs:
|
||||||
|
* 0 : 0x00050080
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
* 2 : Number of notifications
|
||||||
|
*/
|
||||||
|
void GetTotalNotifications(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SetNewsDBHeader service function.
|
||||||
|
* Inputs:
|
||||||
|
* 0 : 0x00060042
|
||||||
|
* 1 : Size
|
||||||
|
* 2 : Input Buffer Mapping Translation Header ((Size << 4) | 0xA)
|
||||||
|
* 3 : Input Buffer Pointer
|
||||||
|
* Outputs:
|
||||||
|
* 0 : 0x00060042
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
void SetNewsDBHeader(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SetNotificationHeader service function.
|
||||||
|
* Inputs:
|
||||||
|
* 0 : 0x00070082
|
||||||
|
* 1 : Notification index
|
||||||
|
* 2 : Size
|
||||||
|
* 3 : Input Buffer Mapping Translation Header ((Size << 4) | 0xA)
|
||||||
|
* 4 : Input Buffer Pointer
|
||||||
|
* Outputs:
|
||||||
|
* 0 : 0x00070042
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
void SetNotificationHeader(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SetNotificationMessage service function.
|
||||||
|
* Inputs:
|
||||||
|
* 0 : 0x00080082
|
||||||
|
* 1 : Notification index
|
||||||
|
* 2 : Size
|
||||||
|
* 3 : Input Buffer Mapping Translation Header ((Size << 4) | 0xA)
|
||||||
|
* 4 : Input Buffer Pointer
|
||||||
|
* Outputs:
|
||||||
|
* 0 : 0x00080042
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
void SetNotificationMessage(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SetNotificationImage service function.
|
||||||
|
* Inputs:
|
||||||
|
* 0 : 0x00090082
|
||||||
|
* 1 : Notification index
|
||||||
|
* 2 : Size
|
||||||
|
* 3 : Input Buffer Mapping Translation Header ((Size << 4) | 0xA)
|
||||||
|
* 4 : Input Buffer Pointer
|
||||||
|
* Outputs:
|
||||||
|
* 0 : 0x00090042
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
void SetNotificationImage(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetNewsDBHeader service function.
|
||||||
|
* Inputs:
|
||||||
|
* 0 : 0x000A0042
|
||||||
|
* 1 : Size
|
||||||
|
* 2 : Output Buffer Mapping Translation Header ((Size << 4) | 0xC)
|
||||||
|
* 3 : Output Buffer Pointer
|
||||||
|
* Outputs:
|
||||||
|
* 0 : 0x000A0082
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
* 2 : Actual Size
|
||||||
|
*/
|
||||||
|
void GetNewsDBHeader(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetNotificationHeader service function.
|
||||||
|
* Inputs:
|
||||||
|
* 0 : 0x000B0082
|
||||||
|
* 1 : Notification index
|
||||||
|
* 2 : Size
|
||||||
|
* 3 : Output Buffer Mapping Translation Header ((Size << 4) | 0xC)
|
||||||
|
* 4 : Output Buffer Pointer
|
||||||
|
* Outputs:
|
||||||
|
* 0 : 0x000B0082
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
* 2 : Actual Size
|
||||||
|
*/
|
||||||
|
void GetNotificationHeader(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetNotificationMessage service function.
|
||||||
|
* Inputs:
|
||||||
|
* 0 : 0x000C0082
|
||||||
|
* 1 : Notification index
|
||||||
|
* 2 : Size
|
||||||
|
* 3 : Output Buffer Mapping Translation Header ((Size << 4) | 0xC)
|
||||||
|
* 4 : Output Buffer Pointer
|
||||||
|
* Outputs:
|
||||||
|
* 0 : 0x000C0082
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
* 2 : Actual Size
|
||||||
|
*/
|
||||||
|
void GetNotificationMessage(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetNotificationImage service function.
|
||||||
|
* Inputs:
|
||||||
|
* 0 : 0x000D0082
|
||||||
|
* 1 : Notification index
|
||||||
|
* 2 : Size
|
||||||
|
* 3 : Output Buffer Mapping Translation Header ((Size << 4) | 0xC)
|
||||||
|
* 4 : Output Buffer Pointer
|
||||||
|
* Outputs:
|
||||||
|
* 0 : 0x000D0082
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
* 2 : Actual Size
|
||||||
|
*/
|
||||||
|
void GetNotificationImage(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SetAutomaticSyncFlag service function.
|
||||||
|
* Inputs:
|
||||||
|
* 0 : 0x00110040
|
||||||
|
* 1 : Flag
|
||||||
|
* Outputs:
|
||||||
|
* 0 : 0x00110040
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
void SetAutomaticSyncFlag(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SetNotificationHeaderOther service function.
|
||||||
|
* Inputs:
|
||||||
|
* 0 : 0x00120082
|
||||||
|
* 1 : Notification index
|
||||||
|
* 2 : Size
|
||||||
|
* 3 : Input Buffer Mapping Translation Header ((Size << 4) | 0xA)
|
||||||
|
* 4 : Input Buffer Pointer
|
||||||
|
* Outputs:
|
||||||
|
* 0 : 0x00120042
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
void SetNotificationHeaderOther(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WriteNewsDBSavedata service function.
|
||||||
|
* Inputs:
|
||||||
|
* 0 : 0x00130000
|
||||||
|
* Outputs:
|
||||||
|
* 0 : 0x00130040
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
void WriteNewsDBSavedata(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetTotalArrivedNotifications service function.
|
||||||
|
* Inputs:
|
||||||
|
* 0 : 0x00140000
|
||||||
|
* Outputs:
|
||||||
|
* 0 : 0x00140080
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
* 2 : Number of pending notifications to be synced
|
||||||
|
*/
|
||||||
|
void GetTotalArrivedNotifications(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::shared_ptr<Module> news;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Gets the total number of notifications
|
||||||
|
* @returns Number of notifications
|
||||||
|
*/
|
||||||
|
std::size_t GetTotalNotifications();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the News DB into the given buffer
|
||||||
|
* @param header The header buffer
|
||||||
|
* @param size The size of the header buffer
|
||||||
|
* @returns Number of bytes read, or error code
|
||||||
|
*/
|
||||||
|
ResultVal<std::size_t> GetNewsDBHeader(NewsDBHeader* header, const std::size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the header for a notification ID into the given buffer
|
||||||
|
* @param notification_index The index of the notification ID
|
||||||
|
* @param header The header buffer
|
||||||
|
* @param size The size of the header buffer
|
||||||
|
* @returns Number of bytes read, or error code
|
||||||
|
*/
|
||||||
|
ResultVal<std::size_t> GetNotificationHeader(const u32 notification_index,
|
||||||
|
NotificationHeader* header,
|
||||||
|
const std::size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the message file for a notification ID and loads it to the message buffer
|
||||||
|
* @param notification_index The index of the notification ID
|
||||||
|
* @param mesasge The message buffer
|
||||||
|
* @returns Number of bytes read, or error code
|
||||||
|
*/
|
||||||
|
ResultVal<std::size_t> GetNotificationMessage(const u32 notification_index,
|
||||||
|
std::span<u8> message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the image file for a notification ID and loads it to the image buffer
|
||||||
|
* @param notification_index The index of the notification ID
|
||||||
|
* @param image The image buffer
|
||||||
|
* @returns Number of bytes read, or error code
|
||||||
|
*/
|
||||||
|
ResultVal<std::size_t> GetNotificationImage(const u32 notification_index, std::span<u8> image);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifies the header for the News DB in memory and saves the News DB file
|
||||||
|
* @param header The database header
|
||||||
|
* @param size The amount of bytes to copy from the header
|
||||||
|
* @returns Result indicating the result of the operation, 0 on success
|
||||||
|
*/
|
||||||
|
Result SetNewsDBHeader(const NewsDBHeader* header, const std::size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifies the header for a notification ID on memory and saves the News DB file
|
||||||
|
* @param notification_index The index of the notification ID
|
||||||
|
* @param header The notification header
|
||||||
|
* @param size The amount of bytes to copy from the header
|
||||||
|
* @returns Result indicating the result of the operation, 0 on success
|
||||||
|
*/
|
||||||
|
Result SetNotificationHeader(const u32 notification_index, const NotificationHeader* header,
|
||||||
|
const std::size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifies the header for a notification ID on memory. The News DB file isn't updated
|
||||||
|
* @param notification_index The index of the notification ID
|
||||||
|
* @param header The notification header
|
||||||
|
* @param size The amount of bytes to copy from the header
|
||||||
|
* @returns Result indicating the result of the operation, 0 on success
|
||||||
|
*/
|
||||||
|
Result SetNotificationHeaderOther(const u32 notification_index,
|
||||||
|
const NotificationHeader* header, const std::size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a given message to a notification ID
|
||||||
|
* @param notification_index The index of the notification ID
|
||||||
|
* @param message The notification message
|
||||||
|
* @returns Result indicating the result of the operation, 0 on success
|
||||||
|
*/
|
||||||
|
Result SetNotificationMessage(const u32 notification_index, std::span<const u8> message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a given image to a notification ID
|
||||||
|
* @param notification_index The index of the notification ID
|
||||||
|
* @param image The notification image
|
||||||
|
* @returns Result indicating the result of the operation, 0 on success
|
||||||
|
*/
|
||||||
|
Result SetNotificationImage(const u32 notification_index, std::span<const u8> image);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new notification with the given data and saves all the contents
|
||||||
|
* @param header The notification header
|
||||||
|
* @param header_size The amount of bytes to copy from the header
|
||||||
|
* @param message The notification message
|
||||||
|
* @param image The notification image
|
||||||
|
* @returns Result indicating the result of the operation, 0 on success
|
||||||
|
*/
|
||||||
|
Result SaveNotification(const NotificationHeader* header, const std::size_t header_size,
|
||||||
|
std::span<const u8> message, std::span<const u8> image);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the given notification ID from the database
|
||||||
|
* @param notification_id The notification ID to delete
|
||||||
|
* @returns Result indicating the result of the operation, 0 on success
|
||||||
|
*/
|
||||||
|
Result DeleteNotification(const u32 notification_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the news.db savedata file and load it to the memory buffer. If the file or the savedata
|
||||||
|
* don't exist, they are created
|
||||||
|
* @returns Result indicating the result of the operation, 0 on success
|
||||||
|
*/
|
||||||
|
Result LoadNewsDBSavedata();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the news.db savedata file to the the NEWS system savedata
|
||||||
|
* @returns Result indicating the result of the operation, 0 on success
|
||||||
|
*/
|
||||||
|
Result SaveNewsDBSavedata();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the file with the given filename inside the NEWS system savedata
|
||||||
|
* @param filename The file to open
|
||||||
|
* @param buffer The buffer to output the contents on
|
||||||
|
* @returns Number of bytes read, or error code
|
||||||
|
*/
|
||||||
|
ResultVal<std::size_t> LoadFileFromSavedata(std::string filename, std::span<u8> buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the file with the given filename inside the NEWS system savedata
|
||||||
|
* @param filename The output file
|
||||||
|
* @param buffer The buffer to read the contents from
|
||||||
|
* @returns Result indicating the result of the operation, 0 on success
|
||||||
|
*/
|
||||||
|
Result SaveFileToSavedata(std::string filename, std::span<const u8> buffer);
|
||||||
|
|
||||||
|
bool CompareNotifications(const u32 first_id, const u32 second_id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Core::System& system;
|
||||||
|
|
||||||
|
NewsDB db{};
|
||||||
|
std::array<u32, MAX_NOTIFICATIONS> notification_ids; // Notifications ordered by date time
|
||||||
|
u8 automatic_sync_flag;
|
||||||
|
std::unique_ptr<FileSys::ArchiveBackend> news_system_save_data_archive;
|
||||||
|
|
||||||
|
template <class Archive>
|
||||||
|
void serialize(Archive& ar, const unsigned int);
|
||||||
|
friend class boost::serialization::access;
|
||||||
|
};
|
||||||
|
|
||||||
void InstallInterfaces(Core::System& system);
|
void InstallInterfaces(Core::System& system);
|
||||||
|
|
||||||
} // namespace Service::NEWS
|
} // namespace Service::NEWS
|
||||||
|
|
||||||
|
SERVICE_CONSTRUCT(Service::NEWS::Module)
|
||||||
|
BOOST_CLASS_EXPORT_KEY(Service::NEWS::Module)
|
||||||
|
@ -3,63 +3,33 @@
|
|||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include "common/archives.h"
|
#include "common/archives.h"
|
||||||
#include "core/hle/ipc_helpers.h"
|
|
||||||
#include "core/hle/service/news/news_s.h"
|
#include "core/hle/service/news/news_s.h"
|
||||||
|
|
||||||
SERIALIZE_EXPORT_IMPL(Service::NEWS::NEWS_S)
|
SERIALIZE_EXPORT_IMPL(Service::NEWS::NEWS_S)
|
||||||
|
|
||||||
namespace Service::NEWS {
|
namespace Service::NEWS {
|
||||||
|
|
||||||
struct NewsDbHeader {
|
NEWS_S::NEWS_S(std::shared_ptr<Module> news) : Module::Interface(std::move(news), "news:s", 2) {
|
||||||
u8 unknown_one;
|
|
||||||
u8 flags;
|
|
||||||
INSERT_PADDING_BYTES(0xE);
|
|
||||||
};
|
|
||||||
static_assert(sizeof(NewsDbHeader) == 0x10, "News DB Header structure size is wrong");
|
|
||||||
|
|
||||||
void NEWS_S::GetTotalNotifications(Kernel::HLERequestContext& ctx) {
|
|
||||||
IPC::RequestParser rp(ctx);
|
|
||||||
|
|
||||||
LOG_WARNING(Service, "(STUBBED) called");
|
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
|
||||||
|
|
||||||
rb.Push(ResultSuccess);
|
|
||||||
rb.Push<u32>(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NEWS_S::GetNewsDBHeader(Kernel::HLERequestContext& ctx) {
|
|
||||||
IPC::RequestParser rp(ctx);
|
|
||||||
const auto size = rp.Pop<u32>();
|
|
||||||
auto output_buffer = rp.PopMappedBuffer();
|
|
||||||
|
|
||||||
LOG_WARNING(Service, "(STUBBED) called size={}", size);
|
|
||||||
|
|
||||||
NewsDbHeader dummy = {.unknown_one = 1, .flags = 0};
|
|
||||||
output_buffer.Write(&dummy, 0, std::min(sizeof(NewsDbHeader), static_cast<std::size_t>(size)));
|
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
|
||||||
|
|
||||||
rb.Push(ResultSuccess);
|
|
||||||
rb.Push<u32>(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
NEWS_S::NEWS_S() : ServiceFramework("news:s", 2) {
|
|
||||||
const FunctionInfo functions[] = {
|
const FunctionInfo functions[] = {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
{0x0001, nullptr, "AddNotification"},
|
{0x0001, &NEWS_S::AddNotificationSystem, "AddNotification"},
|
||||||
|
{0x0004, &NEWS_S::ResetNotifications, "ResetNotifications"},
|
||||||
{0x0005, &NEWS_S::GetTotalNotifications, "GetTotalNotifications"},
|
{0x0005, &NEWS_S::GetTotalNotifications, "GetTotalNotifications"},
|
||||||
{0x0006, nullptr, "SetNewsDBHeader"},
|
{0x0006, &NEWS_S::SetNewsDBHeader, "SetNewsDBHeader"},
|
||||||
{0x0007, nullptr, "SetNotificationHeader"},
|
{0x0007, &NEWS_S::SetNotificationHeader, "SetNotificationHeader"},
|
||||||
{0x0008, nullptr, "SetNotificationMessage"},
|
{0x0008, &NEWS_S::SetNotificationMessage, "SetNotificationMessage"},
|
||||||
{0x0009, nullptr, "SetNotificationImage"},
|
{0x0009, &NEWS_S::SetNotificationImage, "SetNotificationImage"},
|
||||||
{0x000A, &NEWS_S::GetNewsDBHeader, "GetNewsDBHeader"},
|
{0x000A, &NEWS_S::GetNewsDBHeader, "GetNewsDBHeader"},
|
||||||
{0x000B, nullptr, "GetNotificationHeader"},
|
{0x000B, &NEWS_S::GetNotificationHeader, "GetNotificationHeader"},
|
||||||
{0x000C, nullptr, "GetNotificationMessage"},
|
{0x000C, &NEWS_S::GetNotificationMessage, "GetNotificationMessage"},
|
||||||
{0x000D, nullptr, "GetNotificationImage"},
|
{0x000D, &NEWS_S::GetNotificationImage, "GetNotificationImage"},
|
||||||
{0x000E, nullptr, "SetInfoLEDPattern"},
|
{0x000E, nullptr, "SetInfoLEDPattern"},
|
||||||
{0x0012, nullptr, "GetNotificationHeaderOther"},
|
{0x000F, nullptr, "SyncArrivedNotifications"},
|
||||||
{0x0013, nullptr, "WriteNewsDBSavedata"},
|
{0x0010, nullptr, "SyncOneArrivedNotification"},
|
||||||
|
{0x0011, &NEWS_S::SetAutomaticSyncFlag, "SetAutomaticSyncFlag"},
|
||||||
|
{0x0012, &NEWS_S::SetNotificationHeaderOther, "SetNotificationHeaderOther"},
|
||||||
|
{0x0013, &NEWS_S::WriteNewsDBSavedata, "WriteNewsDBSavedata"},
|
||||||
|
{0x0014, &NEWS_S::GetTotalArrivedNotifications, "GetTotalArrivedNotifications"},
|
||||||
// clang-format on
|
// clang-format on
|
||||||
};
|
};
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
|
@ -4,44 +4,19 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include "core/hle/service/news/news.h"
|
||||||
#include "core/hle/service/service.h"
|
|
||||||
|
|
||||||
namespace Service::NEWS {
|
namespace Service::NEWS {
|
||||||
|
|
||||||
class NEWS_S final : public ServiceFramework<NEWS_S> {
|
class NEWS_S final : public Module::Interface {
|
||||||
public:
|
public:
|
||||||
NEWS_S();
|
explicit NEWS_S(std::shared_ptr<Module> news);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
SERVICE_SERIALIZATION(NEWS_S, news, Module)
|
||||||
* GetTotalNotifications service function.
|
|
||||||
* Inputs:
|
|
||||||
* 0 : 0x00050000
|
|
||||||
* Outputs:
|
|
||||||
* 0 : 0x00050080
|
|
||||||
* 1 : Result of function, 0 on success, otherwise error code
|
|
||||||
* 2 : Number of notifications
|
|
||||||
*/
|
|
||||||
void GetTotalNotifications(Kernel::HLERequestContext& ctx);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GetNewsDBHeader service function.
|
|
||||||
* Inputs:
|
|
||||||
* 0 : 0x000A0042
|
|
||||||
* 1 : Size
|
|
||||||
* 2 : Output Buffer Mapping Translation Header ((Size << 4) | 0xC)
|
|
||||||
* 3 : Output Buffer Pointer
|
|
||||||
* Outputs:
|
|
||||||
* 0 : 0x000A0080
|
|
||||||
* 1 : Result of function, 0 on success, otherwise error code
|
|
||||||
* 2 : Actual Size
|
|
||||||
*/
|
|
||||||
void GetNewsDBHeader(Kernel::HLERequestContext& ctx);
|
|
||||||
|
|
||||||
SERVICE_SERIALIZATION_SIMPLE
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::NEWS
|
} // namespace Service::NEWS
|
||||||
|
|
||||||
BOOST_CLASS_EXPORT_KEY(Service::NEWS::NEWS_S)
|
BOOST_CLASS_EXPORT_KEY(Service::NEWS::NEWS_S)
|
||||||
|
BOOST_SERIALIZATION_CONSTRUCT(Service::NEWS::NEWS_S)
|
||||||
|
@ -9,10 +9,10 @@ SERIALIZE_EXPORT_IMPL(Service::NEWS::NEWS_U)
|
|||||||
|
|
||||||
namespace Service::NEWS {
|
namespace Service::NEWS {
|
||||||
|
|
||||||
NEWS_U::NEWS_U() : ServiceFramework("news:u", 1) {
|
NEWS_U::NEWS_U(std::shared_ptr<Module> news) : Module::Interface(std::move(news), "news:u", 1) {
|
||||||
const FunctionInfo functions[] = {
|
const FunctionInfo functions[] = {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
{0x0001, nullptr, "AddNotification"},
|
{0x0001, &NEWS_U::AddNotification, "AddNotification"},
|
||||||
// clang-format on
|
// clang-format on
|
||||||
};
|
};
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
|
@ -4,19 +4,19 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include "core/hle/service/news/news.h"
|
||||||
#include "core/hle/service/service.h"
|
|
||||||
|
|
||||||
namespace Service::NEWS {
|
namespace Service::NEWS {
|
||||||
|
|
||||||
class NEWS_U final : public ServiceFramework<NEWS_U> {
|
class NEWS_U final : public Module::Interface {
|
||||||
public:
|
public:
|
||||||
NEWS_U();
|
explicit NEWS_U(std::shared_ptr<Module> news);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SERVICE_SERIALIZATION_SIMPLE
|
SERVICE_SERIALIZATION(NEWS_U, news, Module)
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::NEWS
|
} // namespace Service::NEWS
|
||||||
|
|
||||||
BOOST_CLASS_EXPORT_KEY(Service::NEWS::NEWS_U)
|
BOOST_CLASS_EXPORT_KEY(Service::NEWS::NEWS_U)
|
||||||
|
BOOST_SERIALIZATION_CONSTRUCT(Service::NEWS::NEWS_U)
|
||||||
|
@ -838,7 +838,7 @@ void SOC_U::Bind(Kernel::HLERequestContext& ctx) {
|
|||||||
SocketHolder& holder = socket_holder_optional->get();
|
SocketHolder& holder = socket_holder_optional->get();
|
||||||
|
|
||||||
CTRSockAddr ctr_sock_addr;
|
CTRSockAddr ctr_sock_addr;
|
||||||
std::memcpy(&ctr_sock_addr, sock_addr_buf.data(), len);
|
std::memcpy(&ctr_sock_addr, sock_addr_buf.data(), std::min<size_t>(len, sizeof(ctr_sock_addr)));
|
||||||
|
|
||||||
sockaddr sock_addr = CTRSockAddr::ToPlatform(ctr_sock_addr);
|
sockaddr sock_addr = CTRSockAddr::ToPlatform(ctr_sock_addr);
|
||||||
|
|
||||||
@ -977,7 +977,7 @@ void SOC_U::Accept(Kernel::HLERequestContext& ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ctr_addr_buf.size() > async_data->max_addr_len) {
|
if (ctr_addr_buf.size() > async_data->max_addr_len) {
|
||||||
LOG_WARNING(Frontend, "CTRSockAddr is too long, truncating data.");
|
LOG_DEBUG(Service_SOC, "CTRSockAddr is too long, truncating data.");
|
||||||
ctr_addr_buf.resize(async_data->max_addr_len);
|
ctr_addr_buf.resize(async_data->max_addr_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1107,7 +1107,8 @@ void SOC_U::SendToOther(Kernel::HLERequestContext& ctx) {
|
|||||||
s32 ret = -1;
|
s32 ret = -1;
|
||||||
if (addr_len > 0) {
|
if (addr_len > 0) {
|
||||||
CTRSockAddr ctr_dest_addr;
|
CTRSockAddr ctr_dest_addr;
|
||||||
std::memcpy(&ctr_dest_addr, dest_addr_buffer.data(), sizeof(ctr_dest_addr));
|
std::memcpy(&ctr_dest_addr, dest_addr_buffer.data(),
|
||||||
|
std::min<size_t>(addr_len, sizeof(ctr_dest_addr)));
|
||||||
sockaddr dest_addr = CTRSockAddr::ToPlatform(ctr_dest_addr);
|
sockaddr dest_addr = CTRSockAddr::ToPlatform(ctr_dest_addr);
|
||||||
ret = static_cast<s32>(::sendto(holder.socket_fd,
|
ret = static_cast<s32>(::sendto(holder.socket_fd,
|
||||||
reinterpret_cast<const char*>(input_buff.data()), len,
|
reinterpret_cast<const char*>(input_buff.data()), len,
|
||||||
@ -1156,7 +1157,8 @@ s32 SOC_U::SendToImpl(SocketHolder& holder, u32 len, u32 flags, u32 addr_len,
|
|||||||
s32 ret = -1;
|
s32 ret = -1;
|
||||||
if (addr_len > 0) {
|
if (addr_len > 0) {
|
||||||
CTRSockAddr ctr_dest_addr;
|
CTRSockAddr ctr_dest_addr;
|
||||||
std::memcpy(&ctr_dest_addr, dest_addr_buff, sizeof(ctr_dest_addr));
|
std::memcpy(&ctr_dest_addr, dest_addr_buff,
|
||||||
|
std::min<size_t>(addr_len, sizeof(ctr_dest_addr)));
|
||||||
sockaddr dest_addr = CTRSockAddr::ToPlatform(ctr_dest_addr);
|
sockaddr dest_addr = CTRSockAddr::ToPlatform(ctr_dest_addr);
|
||||||
ret = static_cast<s32>(::sendto(holder.socket_fd,
|
ret = static_cast<s32>(::sendto(holder.socket_fd,
|
||||||
reinterpret_cast<const char*>(input_buff.data()), len,
|
reinterpret_cast<const char*>(input_buff.data()), len,
|
||||||
@ -1306,7 +1308,8 @@ void SOC_U::RecvFromOther(Kernel::HLERequestContext& ctx) {
|
|||||||
async_data->len, async_data->flags, &src_addr, &src_addr_len));
|
async_data->len, async_data->flags, &src_addr, &src_addr_len));
|
||||||
if (async_data->ret >= 0 && src_addr_len > 0) {
|
if (async_data->ret >= 0 && src_addr_len > 0) {
|
||||||
ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
|
ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
|
||||||
std::memcpy(async_data->addr_buff.data(), &ctr_src_addr, async_data->addr_len);
|
std::memcpy(async_data->addr_buff.data(), &ctr_src_addr,
|
||||||
|
std::min<size_t>(async_data->addr_len, sizeof(ctr_src_addr)));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
async_data->ret = static_cast<s32>(
|
async_data->ret = static_cast<s32>(
|
||||||
@ -1422,7 +1425,8 @@ void SOC_U::RecvFrom(Kernel::HLERequestContext& ctx) {
|
|||||||
async_data->len, async_data->flags, &src_addr, &src_addr_len));
|
async_data->len, async_data->flags, &src_addr, &src_addr_len));
|
||||||
if (async_data->ret >= 0 && src_addr_len > 0) {
|
if (async_data->ret >= 0 && src_addr_len > 0) {
|
||||||
ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
|
ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
|
||||||
std::memcpy(async_data->addr_buff.data(), &ctr_src_addr, async_data->addr_len);
|
std::memcpy(async_data->addr_buff.data(), &ctr_src_addr,
|
||||||
|
std::min<size_t>(async_data->addr_len, sizeof(ctr_src_addr)));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
async_data->ret = static_cast<s32>(
|
async_data->ret = static_cast<s32>(
|
||||||
@ -1566,7 +1570,7 @@ void SOC_U::GetSockName(Kernel::HLERequestContext& ctx) {
|
|||||||
ret = TranslateError(GET_ERRNO);
|
ret = TranslateError(GET_ERRNO);
|
||||||
|
|
||||||
if (dest_addr_buff.size() > max_addr_len) {
|
if (dest_addr_buff.size() > max_addr_len) {
|
||||||
LOG_WARNING(Frontend, "CTRSockAddr is too long, truncating data.");
|
LOG_DEBUG(Service_SOC, "CTRSockAddr is too long, truncating data.");
|
||||||
dest_addr_buff.resize(max_addr_len);
|
dest_addr_buff.resize(max_addr_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1708,7 +1712,7 @@ void SOC_U::GetPeerName(Kernel::HLERequestContext& ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (dest_addr_buff.size() > max_addr_len) {
|
if (dest_addr_buff.size() > max_addr_len) {
|
||||||
LOG_WARNING(Frontend, "CTRSockAddr is too long, truncating data.");
|
LOG_DEBUG(Service_SOC, "CTRSockAddr is too long, truncating data.");
|
||||||
dest_addr_buff.resize(max_addr_len);
|
dest_addr_buff.resize(max_addr_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1724,7 +1728,7 @@ void SOC_U::GetPeerName(Kernel::HLERequestContext& ctx) {
|
|||||||
void SOC_U::Connect(Kernel::HLERequestContext& ctx) {
|
void SOC_U::Connect(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
const auto socket_handle = rp.Pop<u32>();
|
const auto socket_handle = rp.Pop<u32>();
|
||||||
[[maybe_unused]] const auto input_addr_len = rp.Pop<u32>();
|
const auto input_addr_len = rp.Pop<u32>();
|
||||||
const u32 pid = rp.PopPID();
|
const u32 pid = rp.PopPID();
|
||||||
auto input_addr_buf = rp.PopStaticBuffer();
|
auto input_addr_buf = rp.PopStaticBuffer();
|
||||||
|
|
||||||
@ -1751,7 +1755,8 @@ void SOC_U::Connect(Kernel::HLERequestContext& ctx) {
|
|||||||
async_data->pid = pid;
|
async_data->pid = pid;
|
||||||
|
|
||||||
CTRSockAddr ctr_input_addr;
|
CTRSockAddr ctr_input_addr;
|
||||||
std::memcpy(&ctr_input_addr, input_addr_buf.data(), sizeof(ctr_input_addr));
|
std::memcpy(&ctr_input_addr, input_addr_buf.data(),
|
||||||
|
std::min<size_t>(input_addr_len, sizeof(ctr_input_addr)));
|
||||||
|
|
||||||
async_data->input_addr = CTRSockAddr::ToPlatform(ctr_input_addr);
|
async_data->input_addr = CTRSockAddr::ToPlatform(ctr_input_addr);
|
||||||
async_data->socket_handle = socket_handle;
|
async_data->socket_handle = socket_handle;
|
||||||
|
@ -337,7 +337,6 @@ void GeometryPipeline::Reconfigure() {
|
|||||||
// The following assumes that when geometry shader is in use, the shader unit 3 is configured as
|
// The following assumes that when geometry shader is in use, the shader unit 3 is configured as
|
||||||
// a geometry shader unit.
|
// a geometry shader unit.
|
||||||
// TODO: what happens if this is not true?
|
// TODO: what happens if this is not true?
|
||||||
ASSERT(regs.pipeline.gs_unit_exclusive_configuration == 1);
|
|
||||||
ASSERT(regs.gs.shader_mode == ShaderRegs::ShaderMode::GS);
|
ASSERT(regs.gs.shader_mode == ShaderRegs::ShaderMode::GS);
|
||||||
ASSERT(regs.pipeline.use_gs == PipelineRegs::UseGS::Yes);
|
ASSERT(regs.pipeline.use_gs == PipelineRegs::UseGS::Yes);
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ PicaCore::PicaCore(Memory::MemorySystem& memory_, std::shared_ptr<DebugContext>
|
|||||||
gs_unit,
|
gs_unit,
|
||||||
gs_setup},
|
gs_setup},
|
||||||
shader_engine{CreateEngine(Settings::values.use_shader_jit.GetValue())} {
|
shader_engine{CreateEngine(Settings::values.use_shader_jit.GetValue())} {
|
||||||
SetFramebufferDefaults();
|
InitializeRegs();
|
||||||
|
|
||||||
const auto submit_vertex = [this](const AttributeBuffer& buffer) {
|
const auto submit_vertex = [this](const AttributeBuffer& buffer) {
|
||||||
const auto add_triangle = [this](const OutputVertex& v0, const OutputVertex& v1,
|
const auto add_triangle = [this](const OutputVertex& v0, const OutputVertex& v1,
|
||||||
@ -54,7 +54,7 @@ PicaCore::PicaCore(Memory::MemorySystem& memory_, std::shared_ptr<DebugContext>
|
|||||||
|
|
||||||
PicaCore::~PicaCore() = default;
|
PicaCore::~PicaCore() = default;
|
||||||
|
|
||||||
void PicaCore::SetFramebufferDefaults() {
|
void PicaCore::InitializeRegs() {
|
||||||
auto& framebuffer_top = regs.framebuffer_config[0];
|
auto& framebuffer_top = regs.framebuffer_config[0];
|
||||||
auto& framebuffer_sub = regs.framebuffer_config[1];
|
auto& framebuffer_sub = regs.framebuffer_config[1];
|
||||||
|
|
||||||
@ -77,6 +77,11 @@ void PicaCore::SetFramebufferDefaults() {
|
|||||||
framebuffer_sub.stride = 3 * 240;
|
framebuffer_sub.stride = 3 * 240;
|
||||||
framebuffer_sub.color_format.Assign(PixelFormat::RGB8);
|
framebuffer_sub.color_format.Assign(PixelFormat::RGB8);
|
||||||
framebuffer_sub.active_fb = 0;
|
framebuffer_sub.active_fb = 0;
|
||||||
|
|
||||||
|
// Tales of Abyss expects this register to have the following default values.
|
||||||
|
auto& gs = regs.internal.gs;
|
||||||
|
gs.max_input_attribute_index.Assign(1);
|
||||||
|
gs.shader_mode.Assign(ShaderRegs::ShaderMode::VS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PicaCore::BindRasterizer(VideoCore::RasterizerInterface* rasterizer) {
|
void PicaCore::BindRasterizer(VideoCore::RasterizerInterface* rasterizer) {
|
||||||
@ -254,6 +259,13 @@ void PicaCore::WriteInternalReg(u32 id, u32 value, u32 mask) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case PICA_REG_INDEX(vs.output_mask):
|
||||||
|
if (!regs.internal.pipeline.gs_unit_exclusive_configuration &&
|
||||||
|
regs.internal.pipeline.use_gs == PipelineRegs::UseGS::No) {
|
||||||
|
regs.internal.gs.output_mask.Assign(value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case PICA_REG_INDEX(vs.bool_uniforms):
|
case PICA_REG_INDEX(vs.bool_uniforms):
|
||||||
vs_setup.WriteUniformBoolReg(regs.internal.vs.bool_uniforms.Value());
|
vs_setup.WriteUniformBoolReg(regs.internal.vs.bool_uniforms.Value());
|
||||||
if (!regs.internal.pipeline.gs_unit_exclusive_configuration &&
|
if (!regs.internal.pipeline.gs_unit_exclusive_configuration &&
|
||||||
|
@ -39,7 +39,7 @@ public:
|
|||||||
void ProcessCmdList(PAddr list, u32 size);
|
void ProcessCmdList(PAddr list, u32 size);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetFramebufferDefaults();
|
void InitializeRegs();
|
||||||
|
|
||||||
void WriteInternalReg(u32 id, u32 value, u32 mask);
|
void WriteInternalReg(u32 id, u32 value, u32 mask);
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ struct ShaderRegs {
|
|||||||
|
|
||||||
union {
|
union {
|
||||||
// Number of input attributes to shader unit - 1
|
// Number of input attributes to shader unit - 1
|
||||||
|
u32 input_buffer_config;
|
||||||
BitField<0, 4, u32> max_input_attribute_index;
|
BitField<0, 4, u32> max_input_attribute_index;
|
||||||
BitField<8, 8, u32> input_to_uniform;
|
BitField<8, 8, u32> input_to_uniform;
|
||||||
BitField<24, 8, ShaderMode> shader_mode;
|
BitField<24, 8, ShaderMode> shader_mode;
|
||||||
|
@ -76,9 +76,9 @@ public:
|
|||||||
public:
|
public:
|
||||||
Uniforms uniforms;
|
Uniforms uniforms;
|
||||||
PackedAttribute uniform_queue;
|
PackedAttribute uniform_queue;
|
||||||
ProgramCode program_code;
|
ProgramCode program_code{};
|
||||||
SwizzleData swizzle_data;
|
SwizzleData swizzle_data{};
|
||||||
u32 entry_point;
|
u32 entry_point{};
|
||||||
const void* cached_shader{};
|
const void* cached_shader{};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -341,8 +341,8 @@ static constexpr void MortonCopy(u32 width, u32 height, u32 start_offset, u32 en
|
|||||||
*/
|
*/
|
||||||
template <bool decode, PixelFormat format, bool converted = false>
|
template <bool decode, PixelFormat format, bool converted = false>
|
||||||
static constexpr void LinearCopy(std::span<u8> src_buffer, std::span<u8> dst_buffer) {
|
static constexpr void LinearCopy(std::span<u8> src_buffer, std::span<u8> dst_buffer) {
|
||||||
const std::size_t src_size = src_buffer.size();
|
std::size_t src_size = src_buffer.size();
|
||||||
const std::size_t dst_size = dst_buffer.size();
|
std::size_t dst_size = dst_buffer.size();
|
||||||
|
|
||||||
if constexpr (converted) {
|
if constexpr (converted) {
|
||||||
constexpr u32 encoded_bytes_per_pixel = GetFormatBpp(format) / 8;
|
constexpr u32 encoded_bytes_per_pixel = GetFormatBpp(format) / 8;
|
||||||
@ -352,6 +352,9 @@ static constexpr void LinearCopy(std::span<u8> src_buffer, std::span<u8> dst_buf
|
|||||||
constexpr u32 dst_bytes_per_pixel =
|
constexpr u32 dst_bytes_per_pixel =
|
||||||
decode ? decoded_bytes_per_pixel : encoded_bytes_per_pixel;
|
decode ? decoded_bytes_per_pixel : encoded_bytes_per_pixel;
|
||||||
|
|
||||||
|
src_size = Common::AlignDown(src_size, src_bytes_per_pixel);
|
||||||
|
dst_size = Common::AlignDown(dst_size, dst_bytes_per_pixel);
|
||||||
|
|
||||||
for (std::size_t src_index = 0, dst_index = 0; src_index < src_size && dst_index < dst_size;
|
for (std::size_t src_index = 0, dst_index = 0; src_index < src_size && dst_index < dst_size;
|
||||||
src_index += src_bytes_per_pixel, dst_index += dst_bytes_per_pixel) {
|
src_index += src_bytes_per_pixel, dst_index += dst_bytes_per_pixel) {
|
||||||
const auto src_pixel = src_buffer.subspan(src_index, src_bytes_per_pixel);
|
const auto src_pixel = src_buffer.subspan(src_index, src_bytes_per_pixel);
|
||||||
|
@ -629,7 +629,7 @@ void Instance::CreateAllocator() {
|
|||||||
.device = *device,
|
.device = *device,
|
||||||
.pVulkanFunctions = &functions,
|
.pVulkanFunctions = &functions,
|
||||||
.instance = *instance,
|
.instance = *instance,
|
||||||
.vulkanApiVersion = vk::enumerateInstanceVersion(),
|
.vulkanApiVersion = properties.apiVersion,
|
||||||
};
|
};
|
||||||
|
|
||||||
const VkResult result = vmaCreateAllocator(&allocator_info, &allocator);
|
const VkResult result = vmaCreateAllocator(&allocator_info, &allocator);
|
||||||
|
@ -40,7 +40,7 @@ layout (binding = 1, std140) uniform vs_data {
|
|||||||
vec4 clip_coef;
|
vec4 clip_coef;
|
||||||
};
|
};
|
||||||
|
|
||||||
const vec2 EPSILON_Z = vec2(0.00000001f, -1.00001f);
|
const vec2 EPSILON_Z = vec2(0.000001f, -1.00001f);
|
||||||
|
|
||||||
vec4 SanitizeVertex(vec4 vtx_pos) {
|
vec4 SanitizeVertex(vec4 vtx_pos) {
|
||||||
float ndc_z = vtx_pos.z / vtx_pos.w;
|
float ndc_z = vtx_pos.z / vtx_pos.w;
|
||||||
|
Reference in New Issue
Block a user