Compare commits

...

10 Commits

Author SHA1 Message Date
17620f3758 glsl_shader_gen: Increase z=0 epsillon 2024-02-01 01:25:29 +02:00
7a4854c519 shader_setup.h: Initialise program_code (#7396) 2024-01-28 06:02:40 -08:00
d1e3dddf6a core: Fix invalid log formatting in ARM interpreter. (#7391) 2024-01-27 00:39:27 -08:00
265e8193b9 Merge pull request #7392 from amwatson/patch-1
[SettingsFragmentPresenter.kt] correct RESOLUTION_FACTOR key/default
2024-01-26 22:43:04 -05:00
e8c20fa782 [SettingsFragmentPresenter.kt] set RESOLUTION_FACTOR preference with RESOLUTION_FACTOR setting instead of GRAPHICS_API
Currently, the RESOLUTION_FACTOR preference is being set with the GRAPHICS_API key and default. Therefore, it will set/retrieve the wrong values

This revision updates the RESOLUTION_FACTOR preference to use the RESOLUTION_FACTOR key and default value. As a result, RESOLUTION_FACTOR and GRAPHICS_API should store and return the correct (separate) values
2024-01-26 19:14:27 -06:00
95ae46f6a8 SOC_U: Account for variable CTRSockAddr size (#7387)
* SOC_U: Account for variable CTRSockAddr size.

* Apply suggestions
2024-01-26 08:00:19 -08:00
41fe75acb7 renderer_vulkan: Pass physical device API version to VMA instead of instance version. (#7390) 2024-01-26 16:34:12 +02:00
1744537d85 Small improvements to Citra translations (#7379)
* dist: Remove duplicated Finnish translation

For some reason, we had Finnish listed twice on Transifex, causing it be shown twice in Citra.
It has already been deleted again from Transifex, now we only need to remove it from the repo as well.

* citra_qt/configure_ui: Show country of language in the combobox

This prevents an issue where we had seperate versions of the same language for different regions and they were not distinguishable (e.g. "Chinese (China)" and "Chinese (Taiwan)").
2024-01-24 15:17:15 -08:00
bea863efff general: Fixes for Tales of the Abyss (#7381)
* geometry_pipeline: Remove unneeded assert

* Has been hw-tested that gs works correctly even when not in exclusive mode

* pica_core: Propagate output_mask to gs

* Has been hw-tested to occur under the same conditions that other uniforms are shared

* regs_shader: Intialize GPUREG_SH_INPUTBUFFER_CONFIG to default value

* Default value verified on hw. Tales of Abyss does not update the number of vertex attributes for the geometry unit and expects it to be 2

* texture_codec: Align buffer sizes to bpp

* Prevents out of bounds texture reads when launching TOA from the HOME menu

* pica_core: Make default value more clear
2024-01-24 19:22:10 +02:00
89e13a85a7 Implement NEWS service (#7377) 2024-01-24 19:21:48 +02:00
22 changed files with 1298 additions and 6349 deletions

6235
dist/languages/fi_FI.ts vendored

File diff suppressed because it is too large Load Diff

View File

@ -687,8 +687,8 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
1,
10,
"x",
IntSetting.GRAPHICS_API.key,
IntSetting.GRAPHICS_API.defaultValue.toFloat()
IntSetting.RESOLUTION_FACTOR.key,
IntSetting.RESOLUTION_FACTOR.defaultValue.toFloat()
)
)
add(

View File

@ -31,7 +31,8 @@ void ConfigureUi::InitializeLanguageComboBox() {
locale.truncate(locale.lastIndexOf(QLatin1Char{'.'}));
locale.remove(0, locale.lastIndexOf(QLatin1Char{'/'}) + 1);
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

View File

@ -115,6 +115,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Service, Y2R) \
SUB(Service, PS) \
SUB(Service, PLGLDR) \
SUB(Service, NEWS) \
CLS(HW) \
SUB(HW, Memory) \
SUB(HW, LCD) \

View File

@ -82,6 +82,7 @@ enum class Class : u8 {
Service_Y2R, ///< The Y2R (YUV to RGB conversion) service
Service_PS, ///< The PS (Process) service
Service_PLGLDR, ///< The PLGLDR (plugin loader) service
Service_NEWS, ///< The NEWS (Notifications) service
HW, ///< Low-level hardware emulation
HW_Memory, ///< Memory-map and address translation
HW_LCD, ///< LCD register emulation

View File

@ -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 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;
}

View File

@ -64,7 +64,7 @@ static struct vfp_double vfp_double_default_qnan = {
};
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);
}
@ -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))
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?
@ -221,8 +221,7 @@ pack:
vfp_double_dump("pack: final", vd);
{
s64 d = vfp_double_pack(vd);
LOG_TRACE(Core_ARM11, "VFP: {}: d(d{})={:016llx} exceptions={:08x}", func, dd, d,
exceptions);
LOG_TRACE(Core_ARM11, "VFP: {}: d(d{})={:016x} exceptions={:08x}", func, dd, d, exceptions);
vfp_put_double(state, d, dd);
}
return exceptions;

View File

@ -2,18 +2,765 @@
// Licensed under GPLv2 or any later version
// 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/scope_exit.h"
#include "common/string_util.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_u.h"
#include "core/hle/service/service.h"
SERVICE_CONSTRUCT_IMPL(Service::NEWS::Module)
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) {
auto& service_manager = system.ServiceManager();
std::make_shared<NEWS_S>()->InstallAsService(service_manager);
std::make_shared<NEWS_U>()->InstallAsService(service_manager);
auto news = std::make_shared<Module>(system);
std::make_shared<NEWS_S>(news)->InstallAsService(service_manager);
std::make_shared<NEWS_U>(news)->InstallAsService(service_manager);
}
} // namespace Service::NEWS

View File

@ -4,12 +4,482 @@
#pragma once
#include "core/file_sys/archive_backend.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
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);
} // namespace Service::NEWS
SERVICE_CONSTRUCT(Service::NEWS::Module)
BOOST_CLASS_EXPORT_KEY(Service::NEWS::Module)

View File

@ -3,63 +3,33 @@
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/news/news_s.h"
SERIALIZE_EXPORT_IMPL(Service::NEWS::NEWS_S)
namespace Service::NEWS {
struct NewsDbHeader {
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) {
NEWS_S::NEWS_S(std::shared_ptr<Module> news) : Module::Interface(std::move(news), "news:s", 2) {
const FunctionInfo functions[] = {
// clang-format off
{0x0001, nullptr, "AddNotification"},
{0x0001, &NEWS_S::AddNotificationSystem, "AddNotification"},
{0x0004, &NEWS_S::ResetNotifications, "ResetNotifications"},
{0x0005, &NEWS_S::GetTotalNotifications, "GetTotalNotifications"},
{0x0006, nullptr, "SetNewsDBHeader"},
{0x0007, nullptr, "SetNotificationHeader"},
{0x0008, nullptr, "SetNotificationMessage"},
{0x0009, nullptr, "SetNotificationImage"},
{0x0006, &NEWS_S::SetNewsDBHeader, "SetNewsDBHeader"},
{0x0007, &NEWS_S::SetNotificationHeader, "SetNotificationHeader"},
{0x0008, &NEWS_S::SetNotificationMessage, "SetNotificationMessage"},
{0x0009, &NEWS_S::SetNotificationImage, "SetNotificationImage"},
{0x000A, &NEWS_S::GetNewsDBHeader, "GetNewsDBHeader"},
{0x000B, nullptr, "GetNotificationHeader"},
{0x000C, nullptr, "GetNotificationMessage"},
{0x000D, nullptr, "GetNotificationImage"},
{0x000B, &NEWS_S::GetNotificationHeader, "GetNotificationHeader"},
{0x000C, &NEWS_S::GetNotificationMessage, "GetNotificationMessage"},
{0x000D, &NEWS_S::GetNotificationImage, "GetNotificationImage"},
{0x000E, nullptr, "SetInfoLEDPattern"},
{0x0012, nullptr, "GetNotificationHeaderOther"},
{0x0013, nullptr, "WriteNewsDBSavedata"},
{0x000F, nullptr, "SyncArrivedNotifications"},
{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
};
RegisterHandlers(functions);

View File

@ -4,44 +4,19 @@
#pragma once
#include <memory>
#include "core/hle/service/service.h"
#include "core/hle/service/news/news.h"
namespace Service::NEWS {
class NEWS_S final : public ServiceFramework<NEWS_S> {
class NEWS_S final : public Module::Interface {
public:
NEWS_S();
explicit NEWS_S(std::shared_ptr<Module> news);
private:
/**
* 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
SERVICE_SERIALIZATION(NEWS_S, news, Module)
};
} // namespace Service::NEWS
BOOST_CLASS_EXPORT_KEY(Service::NEWS::NEWS_S)
BOOST_SERIALIZATION_CONSTRUCT(Service::NEWS::NEWS_S)

View File

@ -9,10 +9,10 @@ SERIALIZE_EXPORT_IMPL(Service::NEWS::NEWS_U)
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[] = {
// clang-format off
{0x0001, nullptr, "AddNotification"},
{0x0001, &NEWS_U::AddNotification, "AddNotification"},
// clang-format on
};
RegisterHandlers(functions);

View File

@ -4,19 +4,19 @@
#pragma once
#include <memory>
#include "core/hle/service/service.h"
#include "core/hle/service/news/news.h"
namespace Service::NEWS {
class NEWS_U final : public ServiceFramework<NEWS_U> {
class NEWS_U final : public Module::Interface {
public:
NEWS_U();
explicit NEWS_U(std::shared_ptr<Module> news);
private:
SERVICE_SERIALIZATION_SIMPLE
SERVICE_SERIALIZATION(NEWS_U, news, Module)
};
} // namespace Service::NEWS
BOOST_CLASS_EXPORT_KEY(Service::NEWS::NEWS_U)
BOOST_SERIALIZATION_CONSTRUCT(Service::NEWS::NEWS_U)

View File

@ -838,7 +838,7 @@ void SOC_U::Bind(Kernel::HLERequestContext& ctx) {
SocketHolder& holder = socket_holder_optional->get();
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);
@ -977,7 +977,7 @@ void SOC_U::Accept(Kernel::HLERequestContext& ctx) {
}
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);
}
@ -1107,7 +1107,8 @@ void SOC_U::SendToOther(Kernel::HLERequestContext& ctx) {
s32 ret = -1;
if (addr_len > 0) {
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);
ret = static_cast<s32>(::sendto(holder.socket_fd,
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;
if (addr_len > 0) {
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);
ret = static_cast<s32>(::sendto(holder.socket_fd,
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));
if (async_data->ret >= 0 && src_addr_len > 0) {
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 {
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));
if (async_data->ret >= 0 && src_addr_len > 0) {
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 {
async_data->ret = static_cast<s32>(
@ -1566,7 +1570,7 @@ void SOC_U::GetSockName(Kernel::HLERequestContext& ctx) {
ret = TranslateError(GET_ERRNO);
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);
}
@ -1708,7 +1712,7 @@ void SOC_U::GetPeerName(Kernel::HLERequestContext& ctx) {
}
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);
}
@ -1724,7 +1728,7 @@ void SOC_U::GetPeerName(Kernel::HLERequestContext& ctx) {
void SOC_U::Connect(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
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();
auto input_addr_buf = rp.PopStaticBuffer();
@ -1751,7 +1755,8 @@ void SOC_U::Connect(Kernel::HLERequestContext& ctx) {
async_data->pid = pid;
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->socket_handle = socket_handle;

View File

@ -337,7 +337,6 @@ void GeometryPipeline::Reconfigure() {
// The following assumes that when geometry shader is in use, the shader unit 3 is configured as
// a geometry shader unit.
// 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.pipeline.use_gs == PipelineRegs::UseGS::Yes);

View File

@ -35,7 +35,7 @@ PicaCore::PicaCore(Memory::MemorySystem& memory_, std::shared_ptr<DebugContext>
gs_unit,
gs_setup},
shader_engine{CreateEngine(Settings::values.use_shader_jit.GetValue())} {
SetFramebufferDefaults();
InitializeRegs();
const auto submit_vertex = [this](const AttributeBuffer& buffer) {
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;
void PicaCore::SetFramebufferDefaults() {
void PicaCore::InitializeRegs() {
auto& framebuffer_top = regs.framebuffer_config[0];
auto& framebuffer_sub = regs.framebuffer_config[1];
@ -77,6 +77,11 @@ void PicaCore::SetFramebufferDefaults() {
framebuffer_sub.stride = 3 * 240;
framebuffer_sub.color_format.Assign(PixelFormat::RGB8);
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) {
@ -254,6 +259,13 @@ void PicaCore::WriteInternalReg(u32 id, u32 value, u32 mask) {
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):
vs_setup.WriteUniformBoolReg(regs.internal.vs.bool_uniforms.Value());
if (!regs.internal.pipeline.gs_unit_exclusive_configuration &&

View File

@ -39,7 +39,7 @@ public:
void ProcessCmdList(PAddr list, u32 size);
private:
void SetFramebufferDefaults();
void InitializeRegs();
void WriteInternalReg(u32 id, u32 value, u32 mask);

View File

@ -35,6 +35,7 @@ struct ShaderRegs {
union {
// Number of input attributes to shader unit - 1
u32 input_buffer_config;
BitField<0, 4, u32> max_input_attribute_index;
BitField<8, 8, u32> input_to_uniform;
BitField<24, 8, ShaderMode> shader_mode;

View File

@ -76,9 +76,9 @@ public:
public:
Uniforms uniforms;
PackedAttribute uniform_queue;
ProgramCode program_code;
SwizzleData swizzle_data;
u32 entry_point;
ProgramCode program_code{};
SwizzleData swizzle_data{};
u32 entry_point{};
const void* cached_shader{};
private:

View File

@ -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>
static constexpr void LinearCopy(std::span<u8> src_buffer, std::span<u8> dst_buffer) {
const std::size_t src_size = src_buffer.size();
const std::size_t dst_size = dst_buffer.size();
std::size_t src_size = src_buffer.size();
std::size_t dst_size = dst_buffer.size();
if constexpr (converted) {
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 =
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;
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);

View File

@ -629,7 +629,7 @@ void Instance::CreateAllocator() {
.device = *device,
.pVulkanFunctions = &functions,
.instance = *instance,
.vulkanApiVersion = vk::enumerateInstanceVersion(),
.vulkanApiVersion = properties.apiVersion,
};
const VkResult result = vmaCreateAllocator(&allocator_info, &allocator);

View File

@ -40,7 +40,7 @@ layout (binding = 1, std140) uniform vs_data {
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) {
float ndc_z = vtx_pos.z / vtx_pos.w;