Implement NEWS service (#7377)
This commit is contained in:
		
				
					committed by
					
						
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							549fdd0736
						
					
				
				
					commit
					89e13a85a7
				
			@@ -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)                                                                                   \
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user