Service/CFG: convert to ServiceFramework
This commit is contained in:
@@ -5,15 +5,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
|
||||
union ResultCode;
|
||||
#include "core/hle/service/fs/archive.h"
|
||||
|
||||
namespace Service {
|
||||
|
||||
class Interface;
|
||||
|
||||
namespace CFG {
|
||||
|
||||
enum SystemModel {
|
||||
@@ -82,289 +79,321 @@ static const std::array<u16, 187> country_codes = {{
|
||||
C("SM"), C("VA"), C("BM"), // 184-186
|
||||
}};
|
||||
|
||||
/**
|
||||
* CFG::GetCountryCodeString service function
|
||||
* Inputs:
|
||||
* 1 : Country Code ID
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : Country's 2-char string
|
||||
*/
|
||||
void GetCountryCodeString(Service::Interface* self);
|
||||
class Module final {
|
||||
public:
|
||||
Module();
|
||||
~Module();
|
||||
|
||||
/**
|
||||
* CFG::GetCountryCodeID service function
|
||||
* Inputs:
|
||||
* 1 : Country Code 2-char string
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : Country Code ID
|
||||
*/
|
||||
void GetCountryCodeID(Service::Interface* self);
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
Interface(std::shared_ptr<Module> cfg, const char* name, u32 max_session);
|
||||
~Interface();
|
||||
|
||||
u32 GetRegionValue();
|
||||
/**
|
||||
* CFG::GetCountryCodeString service function
|
||||
* Inputs:
|
||||
* 1 : Country Code ID
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : Country's 2-char string
|
||||
*/
|
||||
void GetCountryCodeString(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* CFG::SecureInfoGetRegion service function
|
||||
* Inputs:
|
||||
* 1 : None
|
||||
* Outputs:
|
||||
* 0 : Result Header code
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : Region value loaded from SecureInfo offset 0x100
|
||||
*/
|
||||
void SecureInfoGetRegion(Service::Interface* self);
|
||||
/**
|
||||
* CFG::GetCountryCodeID service function
|
||||
* Inputs:
|
||||
* 1 : Country Code 2-char string
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : Country Code ID
|
||||
*/
|
||||
void GetCountryCodeID(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* CFG::GenHashConsoleUnique service function
|
||||
* Inputs:
|
||||
* 1 : 20 bit application ID salt
|
||||
* Outputs:
|
||||
* 0 : Result Header code
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : Hash/"ID" lower word
|
||||
* 3 : Hash/"ID" upper word
|
||||
*/
|
||||
void GenHashConsoleUnique(Service::Interface* self);
|
||||
u32 GetRegionValue();
|
||||
|
||||
/**
|
||||
* CFG::GetRegionCanadaUSA service function
|
||||
* Inputs:
|
||||
* 1 : None
|
||||
* Outputs:
|
||||
* 0 : Result Header code
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : 1 if the system is a Canada or USA model, 0 otherwise
|
||||
*/
|
||||
void GetRegionCanadaUSA(Service::Interface* self);
|
||||
/**
|
||||
* CFG::SecureInfoGetRegion service function
|
||||
* Inputs:
|
||||
* 1 : None
|
||||
* Outputs:
|
||||
* 0 : Result Header code
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : Region value loaded from SecureInfo offset 0x100
|
||||
*/
|
||||
void SecureInfoGetRegion(Kernel::HLERequestContext& ctx, u16 id);
|
||||
|
||||
/**
|
||||
* CFG::GetSystemModel service function
|
||||
* Inputs:
|
||||
* 0 : 0x00050000
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : Model of the console
|
||||
*/
|
||||
void GetSystemModel(Service::Interface* self);
|
||||
/**
|
||||
* CFG::GenHashConsoleUnique service function
|
||||
* Inputs:
|
||||
* 1 : 20 bit application ID salt
|
||||
* Outputs:
|
||||
* 0 : Result Header code
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : Hash/"ID" lower word
|
||||
* 3 : Hash/"ID" upper word
|
||||
*/
|
||||
void GenHashConsoleUnique(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* CFG::GetModelNintendo2DS service function
|
||||
* Inputs:
|
||||
* 0 : 0x00060000
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : 0 if the system is a Nintendo 2DS, 1 otherwise
|
||||
*/
|
||||
void GetModelNintendo2DS(Service::Interface* self);
|
||||
/**
|
||||
* CFG::GetRegionCanadaUSA service function
|
||||
* Inputs:
|
||||
* 1 : None
|
||||
* Outputs:
|
||||
* 0 : Result Header code
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : 1 if the system is a Canada or USA model, 0 otherwise
|
||||
*/
|
||||
void GetRegionCanadaUSA(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* CFG::GetConfigInfoBlk2 service function
|
||||
* Inputs:
|
||||
* 0 : 0x00010082
|
||||
* 1 : Size
|
||||
* 2 : Block ID
|
||||
* 3 : Descriptor for the output buffer
|
||||
* 4 : Output buffer pointer
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
*/
|
||||
void GetConfigInfoBlk2(Service::Interface* self);
|
||||
/**
|
||||
* CFG::GetSystemModel service function
|
||||
* Inputs:
|
||||
* 0 : 0x00050000
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : Model of the console
|
||||
*/
|
||||
void GetSystemModel(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* CFG::GetConfigInfoBlk8 service function
|
||||
* Inputs:
|
||||
* 0 : 0x04010082 / 0x08010082
|
||||
* 1 : Size
|
||||
* 2 : Block ID
|
||||
* 3 : Descriptor for the output buffer
|
||||
* 4 : Output buffer pointer
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
*/
|
||||
void GetConfigInfoBlk8(Service::Interface* self);
|
||||
/**
|
||||
* CFG::GetModelNintendo2DS service function
|
||||
* Inputs:
|
||||
* 0 : 0x00060000
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : 0 if the system is a Nintendo 2DS, 1 otherwise
|
||||
*/
|
||||
void GetModelNintendo2DS(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* CFG::SetConfigInfoBlk4 service function
|
||||
* Inputs:
|
||||
* 0 : 0x04020082 / 0x08020082
|
||||
* 1 : Block ID
|
||||
* 2 : Size
|
||||
* 3 : Descriptor for the output buffer
|
||||
* 4 : Output buffer pointer
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* Note:
|
||||
* The parameters order is different from GetConfigInfoBlk2/8's,
|
||||
* where Block ID and Size are switched.
|
||||
*/
|
||||
void SetConfigInfoBlk4(Service::Interface* self);
|
||||
/**
|
||||
* CFG::GetConfigInfoBlk2 service function
|
||||
* Inputs:
|
||||
* 0 : 0x00010082
|
||||
* 1 : Size
|
||||
* 2 : Block ID
|
||||
* 3 : Descriptor for the output buffer
|
||||
* 4 : Output buffer pointer
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
*/
|
||||
void GetConfigInfoBlk2(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* CFG::UpdateConfigNANDSavegame service function
|
||||
* Inputs:
|
||||
* 0 : 0x04030000 / 0x08030000
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
*/
|
||||
void UpdateConfigNANDSavegame(Service::Interface* self);
|
||||
/**
|
||||
* CFG::GetConfigInfoBlk8 service function
|
||||
* Inputs:
|
||||
* 0 : 0x04010082 / 0x08010082
|
||||
* 1 : Size
|
||||
* 2 : Block ID
|
||||
* 3 : Descriptor for the output buffer
|
||||
* 4 : Output buffer pointer
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
*/
|
||||
void GetConfigInfoBlk8(Kernel::HLERequestContext& ctx, u16 id);
|
||||
|
||||
/**
|
||||
* CFG::FormatConfig service function
|
||||
* Inputs:
|
||||
* 0 : 0x08060000
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
*/
|
||||
void FormatConfig(Service::Interface* self);
|
||||
/**
|
||||
* CFG::SetConfigInfoBlk4 service function
|
||||
* Inputs:
|
||||
* 0 : 0x04020082 / 0x08020082
|
||||
* 1 : Block ID
|
||||
* 2 : Size
|
||||
* 3 : Descriptor for the output buffer
|
||||
* 4 : Output buffer pointer
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* Note:
|
||||
* The parameters order is different from GetConfigInfoBlk2/8's,
|
||||
* where Block ID and Size are switched.
|
||||
*/
|
||||
void SetConfigInfoBlk4(Kernel::HLERequestContext& ctx, u16 id);
|
||||
|
||||
/**
|
||||
* Reads a block with the specified id and flag from the Config savegame buffer
|
||||
* and writes the output to output. The input size must match exactly the size of the requested
|
||||
* block.
|
||||
*
|
||||
* @param block_id The id of the block we want to read
|
||||
* @param size The size of the block we want to read
|
||||
* @param flag The requested block must have this flag set
|
||||
* @param output A pointer where we will write the read data
|
||||
* @returns ResultCode indicating the result of the operation, 0 on success
|
||||
*/
|
||||
ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, void* output);
|
||||
/**
|
||||
* CFG::UpdateConfigNANDSavegame service function
|
||||
* Inputs:
|
||||
* 0 : 0x04030000 / 0x08030000
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
*/
|
||||
void UpdateConfigNANDSavegame(Kernel::HLERequestContext& ctx, u16 id);
|
||||
|
||||
/**
|
||||
* Reads data from input and writes to a block with the specified id and flag
|
||||
* in the Config savegame buffer. The input size must match exactly the size of the target block.
|
||||
*
|
||||
* @param block_id The id of the block we want to write
|
||||
* @param size The size of the block we want to write
|
||||
* @param flag The target block must have this flag set
|
||||
* @param input A pointer where we will read data and write to Config savegame buffer
|
||||
* @returns ResultCode indicating the result of the operation, 0 on success
|
||||
*/
|
||||
ResultCode SetConfigInfoBlock(u32 block_id, u32 size, u32 flag, const void* input);
|
||||
/**
|
||||
* CFG::FormatConfig service function
|
||||
* Inputs:
|
||||
* 0 : 0x08060000
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
*/
|
||||
void FormatConfig(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* Creates a block with the specified id and writes the input data to the cfg savegame buffer in
|
||||
* memory. The config savegame file in the filesystem is not updated.
|
||||
*
|
||||
* @param block_id The id of the block we want to create
|
||||
* @param size The size of the block we want to create
|
||||
* @param flags The flags of the new block
|
||||
* @param data A pointer containing the data we will write to the new block
|
||||
* @returns ResultCode indicating the result of the operation, 0 on success
|
||||
*/
|
||||
ResultCode CreateConfigInfoBlk(u32 block_id, u16 size, u16 flags, const void* data);
|
||||
/// A helper function for dispatching service functions that have multiple IDs
|
||||
template <void (Interface::*function)(Kernel::HLERequestContext& ctx, u16 id), u16 id>
|
||||
void D(Kernel::HLERequestContext& ctx) {
|
||||
(this->*function)(ctx, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the config savegame file from the filesystem, the buffer in memory is not affected
|
||||
* @returns ResultCode indicating the result of the operation, 0 on success
|
||||
*/
|
||||
ResultCode DeleteConfigNANDSaveFile();
|
||||
private:
|
||||
std::shared_ptr<Module> cfg;
|
||||
};
|
||||
|
||||
/**
|
||||
* Writes the config savegame memory buffer to the config savegame file in the filesystem
|
||||
* @returns ResultCode indicating the result of the operation, 0 on success
|
||||
*/
|
||||
ResultCode UpdateConfigNANDSavegame();
|
||||
private:
|
||||
ResultVal<void*> GetConfigInfoBlockPointer(u32 block_id, u32 size, u32 flag);
|
||||
|
||||
/**
|
||||
* Re-creates the config savegame file in memory and the filesystem with the default blocks
|
||||
* @returns ResultCode indicating the result of the operation, 0 on success
|
||||
*/
|
||||
ResultCode FormatConfig();
|
||||
/**
|
||||
* Reads a block with the specified id and flag from the Config savegame buffer
|
||||
* and writes the output to output. The input size must match exactly the size of the requested
|
||||
* block.
|
||||
*
|
||||
* @param block_id The id of the block we want to read
|
||||
* @param size The size of the block we want to read
|
||||
* @param flag The requested block must have this flag set
|
||||
* @param output A pointer where we will write the read data
|
||||
* @returns ResultCode indicating the result of the operation, 0 on success
|
||||
*/
|
||||
ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, void* output);
|
||||
|
||||
/**
|
||||
* Open the config savegame file and load it to the memory buffer
|
||||
* @returns ResultCode indicating the result of the operation, 0 on success
|
||||
*/
|
||||
ResultCode LoadConfigNANDSaveFile();
|
||||
/**
|
||||
* Reads data from input and writes to a block with the specified id and flag
|
||||
* in the Config savegame buffer. The input size must match exactly the size of the target
|
||||
* block.
|
||||
*
|
||||
* @param block_id The id of the block we want to write
|
||||
* @param size The size of the block we want to write
|
||||
* @param flag The target block must have this flag set
|
||||
* @param input A pointer where we will read data and write to Config savegame buffer
|
||||
* @returns ResultCode indicating the result of the operation, 0 on success
|
||||
*/
|
||||
ResultCode SetConfigInfoBlock(u32 block_id, u32 size, u32 flag, const void* input);
|
||||
|
||||
/// Initialize the config service
|
||||
void Init();
|
||||
/**
|
||||
* Creates a block with the specified id and writes the input data to the cfg savegame buffer in
|
||||
* memory. The config savegame file in the filesystem is not updated.
|
||||
*
|
||||
* @param block_id The id of the block we want to create
|
||||
* @param size The size of the block we want to create
|
||||
* @param flags The flags of the new block
|
||||
* @param data A pointer containing the data we will write to the new block
|
||||
* @returns ResultCode indicating the result of the operation, 0 on success
|
||||
*/
|
||||
ResultCode CreateConfigInfoBlk(u32 block_id, u16 size, u16 flags, const void* data);
|
||||
|
||||
/// Shutdown the config service
|
||||
void Shutdown();
|
||||
/**
|
||||
* Deletes the config savegame file from the filesystem, the buffer in memory is not affected
|
||||
* @returns ResultCode indicating the result of the operation, 0 on success
|
||||
*/
|
||||
ResultCode DeleteConfigNANDSaveFile();
|
||||
|
||||
/**
|
||||
* Set the region code preferred by the game so that CFG will adjust to it when the region setting
|
||||
* is auto.
|
||||
* @param region_code the preferred region code to set
|
||||
*/
|
||||
void SetPreferredRegionCode(u32 region_code);
|
||||
/**
|
||||
* Re-creates the config savegame file in memory and the filesystem with the default blocks
|
||||
* @returns ResultCode indicating the result of the operation, 0 on success
|
||||
*/
|
||||
ResultCode FormatConfig();
|
||||
|
||||
// Utilities for frontend to set config data.
|
||||
// Note: before calling these functions, LoadConfigNANDSaveFile should be called,
|
||||
// and UpdateConfigNANDSavegame should be called after making changes to config data.
|
||||
/**
|
||||
* Open the config savegame file and load it to the memory buffer
|
||||
* @returns ResultCode indicating the result of the operation, 0 on success
|
||||
*/
|
||||
ResultCode LoadConfigNANDSaveFile();
|
||||
|
||||
/**
|
||||
* Sets the username in config savegame.
|
||||
* @param name the username to set. The maximum size is 10 in char16_t.
|
||||
*/
|
||||
void SetUsername(const std::u16string& name);
|
||||
public:
|
||||
u32 GetRegionValue();
|
||||
|
||||
/**
|
||||
* Gets the username from config savegame.
|
||||
* @returns the username
|
||||
*/
|
||||
std::u16string GetUsername();
|
||||
/**
|
||||
* Set the region code preferred by the game so that CFG will adjust to it when the region
|
||||
* setting
|
||||
* is auto.
|
||||
* @param region_code the preferred region code to set
|
||||
*/
|
||||
void SetPreferredRegionCode(u32 region_code);
|
||||
|
||||
/**
|
||||
* Sets the profile birthday in config savegame.
|
||||
* @param month the month of birthday.
|
||||
* @param day the day of the birthday.
|
||||
*/
|
||||
void SetBirthday(u8 month, u8 day);
|
||||
// Utilities for frontend to set config data.
|
||||
// Note: UpdateConfigNANDSavegame should be called after making changes to config data.
|
||||
|
||||
/**
|
||||
* Gets the profile birthday from the config savegame.
|
||||
* @returns a tuple of (month, day) of birthday
|
||||
*/
|
||||
std::tuple<u8, u8> GetBirthday();
|
||||
/**
|
||||
* Sets the username in config savegame.
|
||||
* @param name the username to set. The maximum size is 10 in char16_t.
|
||||
*/
|
||||
void SetUsername(const std::u16string& name);
|
||||
|
||||
/**
|
||||
* Sets the system language in config savegame.
|
||||
* @param language the system language to set.
|
||||
*/
|
||||
void SetSystemLanguage(SystemLanguage language);
|
||||
/**
|
||||
* Gets the username from config savegame.
|
||||
* @returns the username
|
||||
*/
|
||||
std::u16string GetUsername();
|
||||
|
||||
/**
|
||||
* Gets the system language from config savegame.
|
||||
* @returns the system language
|
||||
*/
|
||||
SystemLanguage GetSystemLanguage();
|
||||
/**
|
||||
* Sets the profile birthday in config savegame.
|
||||
* @param month the month of birthday.
|
||||
* @param day the day of the birthday.
|
||||
*/
|
||||
void SetBirthday(u8 month, u8 day);
|
||||
|
||||
/**
|
||||
* Sets the sound output mode in config savegame.
|
||||
* @param mode the sound output mode to set
|
||||
*/
|
||||
void SetSoundOutputMode(SoundOutputMode mode);
|
||||
/**
|
||||
* Gets the profile birthday from the config savegame.
|
||||
* @returns a tuple of (month, day) of birthday
|
||||
*/
|
||||
std::tuple<u8, u8> GetBirthday();
|
||||
|
||||
/**
|
||||
* Gets the sound output mode from config savegame.
|
||||
* @returns the sound output mode
|
||||
*/
|
||||
SoundOutputMode GetSoundOutputMode();
|
||||
/**
|
||||
* Sets the system language in config savegame.
|
||||
* @param language the system language to set.
|
||||
*/
|
||||
void SetSystemLanguage(SystemLanguage language);
|
||||
|
||||
/**
|
||||
* Generates a new random console unique id.
|
||||
* @param random_number a random generated 16bit number stored at 0x90002, used for generating the
|
||||
* console_id
|
||||
* @param console_id the randomly created console id
|
||||
*/
|
||||
void GenerateConsoleUniqueId(u32& random_number, u64& console_id);
|
||||
/**
|
||||
* Gets the system language from config savegame.
|
||||
* @returns the system language
|
||||
*/
|
||||
SystemLanguage GetSystemLanguage();
|
||||
|
||||
/**
|
||||
* Sets the random_number and the console unique id in the config savegame.
|
||||
* @param random_number the random_number to set
|
||||
* @param console_id the console id to set
|
||||
*/
|
||||
ResultCode SetConsoleUniqueId(u32 random_number, u64 console_id);
|
||||
/**
|
||||
* Sets the sound output mode in config savegame.
|
||||
* @param mode the sound output mode to set
|
||||
*/
|
||||
void SetSoundOutputMode(SoundOutputMode mode);
|
||||
|
||||
/**
|
||||
* Gets the console unique id from config savegame.
|
||||
* @returns the console unique id
|
||||
*/
|
||||
u64 GetConsoleUniqueId();
|
||||
/**
|
||||
* Gets the sound output mode from config savegame.
|
||||
* @returns the sound output mode
|
||||
*/
|
||||
SoundOutputMode GetSoundOutputMode();
|
||||
|
||||
/**
|
||||
* Generates a new random console unique id.
|
||||
* @param random_number a random generated 16bit number stored at 0x90002, used for generating
|
||||
* the
|
||||
* console_id
|
||||
* @param console_id the randomly created console id
|
||||
*/
|
||||
void GenerateConsoleUniqueId(u32& random_number, u64& console_id);
|
||||
|
||||
/**
|
||||
* Sets the random_number and the console unique id in the config savegame.
|
||||
* @param random_number the random_number to set
|
||||
* @param console_id the console id to set
|
||||
*/
|
||||
ResultCode SetConsoleUniqueId(u32 random_number, u64 console_id);
|
||||
|
||||
/**
|
||||
* Gets the console unique id from config savegame.
|
||||
* @returns the console unique id
|
||||
*/
|
||||
u64 GetConsoleUniqueId();
|
||||
|
||||
/**
|
||||
* Writes the config savegame memory buffer to the config savegame file in the filesystem
|
||||
* @returns ResultCode indicating the result of the operation, 0 on success
|
||||
*/
|
||||
ResultCode UpdateConfigNANDSavegame();
|
||||
|
||||
private:
|
||||
static constexpr u32 CONFIG_SAVEFILE_SIZE = 0x8000;
|
||||
std::array<u8, CONFIG_SAVEFILE_SIZE> cfg_config_file_buffer;
|
||||
Service::FS::ArchiveHandle cfg_system_save_data_archive;
|
||||
u32 preferred_region_code = 0;
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
std::shared_ptr<Module> GetCurrentModule();
|
||||
|
||||
} // namespace CFG
|
||||
} // namespace Service
|
||||
|
||||
Reference in New Issue
Block a user