Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
d21b403886 | |||
5afd521c5a | |||
0c66d71fe8 | |||
bdc4fa81f2 | |||
625f5fb88a | |||
2382717600 | |||
30ee70a9bc |
552
Ryujinx.Ava/Assets/Locales/zh_CN.json
Normal file
552
Ryujinx.Ava/Assets/Locales/zh_CN.json
Normal file
@ -0,0 +1,552 @@
|
|||||||
|
{
|
||||||
|
"MenuBarFileOpenApplet": "打开小程序",
|
||||||
|
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "打开独立的Mii小程序",
|
||||||
|
"SettingsTabInputDirectMouseAccess": "直通鼠标操作",
|
||||||
|
"SettingsTabSystemMemoryManagerMode": "内存管理模式:",
|
||||||
|
"SettingsTabSystemMemoryManagerModeSoftware": "软件",
|
||||||
|
"SettingsTabSystemMemoryManagerModeHost": "本机 (快速)",
|
||||||
|
"SettingsTabSystemMemoryManagerModeHostUnchecked": "跳过检查的本机 (最快速)",
|
||||||
|
"MenuBarFile": "文件(_F)",
|
||||||
|
"MenuBarFileOpenFromFile": "加载文件中的程序(_L)",
|
||||||
|
"MenuBarFileOpenUnpacked": "加载解包后的游戏(_U)",
|
||||||
|
"MenuBarFileOpenEmuFolder": "打开Ryujinx文件夹",
|
||||||
|
"MenuBarFileOpenLogsFolder": "打开日志文件夹",
|
||||||
|
"MenuBarFileExit": "退出(_E)",
|
||||||
|
"MenuBarOptions": "选项",
|
||||||
|
"MenuBarOptionsToggleFullscreen": "切换全屏",
|
||||||
|
"MenuBarOptionsStartGamesInFullscreen": "以全屏模式启动游戏",
|
||||||
|
"MenuBarOptionsStopEmulation": "中止模拟",
|
||||||
|
"MenuBarOptionsSettings": "设置(_S)",
|
||||||
|
"MenuBarOptionsManageUserProfiles": "管理用户账户(_M)",
|
||||||
|
"MenuBarActions": "行动(_A)",
|
||||||
|
"MenuBarOptionsSimulateWakeUpMessage": "模拟唤醒消息",
|
||||||
|
"MenuBarActionsScanAmiibo": "扫描Amiibo",
|
||||||
|
"MenuBarTools": "工具(_T)",
|
||||||
|
"MenuBarToolsInstallFirmware": "安装固件",
|
||||||
|
"MenuBarFileToolsInstallFirmwareFromFile": "从 XCI 或 ZIP 安装固件",
|
||||||
|
"MenuBarFileToolsInstallFirmwareFromDirectory": "从文件夹安装固件",
|
||||||
|
"MenuBarHelp": "帮助",
|
||||||
|
"MenuBarHelpCheckForUpdates": "检查更新",
|
||||||
|
"MenuBarHelpAbout": "关于",
|
||||||
|
"MenuSearch": "搜索...",
|
||||||
|
"GameListHeaderFavorite": "收藏",
|
||||||
|
"GameListHeaderIcon": "图标",
|
||||||
|
"GameListHeaderApplication": "名称",
|
||||||
|
"GameListHeaderDeveloper": "制作商",
|
||||||
|
"GameListHeaderVersion": "版本",
|
||||||
|
"GameListHeaderTimePlayed": "游玩时间",
|
||||||
|
"GameListHeaderLastPlayed": "上次游玩",
|
||||||
|
"GameListHeaderFileExtension": "扩展名",
|
||||||
|
"GameListHeaderFileSize": "大小",
|
||||||
|
"GameListHeaderPath": "路径",
|
||||||
|
"GameListContextMenuOpenUserSaveDirectory": "打开应用存档目录",
|
||||||
|
"GameListContextMenuOpenUserSaveDirectoryToolTip": "打开储存游戏存档的目录",
|
||||||
|
"GameListContextMenuOpenUserDeviceDirectory": "打开应用系统目录",
|
||||||
|
"GameListContextMenuOpenUserDeviceDirectoryToolTip": "打开包含游戏系统设置的目录",
|
||||||
|
"GameListContextMenuOpenUserBcatDirectory": "打开BCAT目录",
|
||||||
|
"GameListContextMenuOpenUserBcatDirectoryToolTip": "打开包含游戏BCAT数据的目录",
|
||||||
|
"GameListContextMenuManageTitleUpdates": "管理游戏更新",
|
||||||
|
"GameListContextMenuManageTitleUpdatesToolTip": "打开更新管理窗口",
|
||||||
|
"GameListContextMenuManageDlc": "管理DLC",
|
||||||
|
"GameListContextMenuManageDlcToolTip": "打开 DLC 管理窗口",
|
||||||
|
"GameListContextMenuOpenModsDirectory": "打开MOD目录",
|
||||||
|
"GameListContextMenuOpenModsDirectoryToolTip": "打开存放游戏MOD的目录",
|
||||||
|
"GameListContextMenuCacheManagement": "缓存管理",
|
||||||
|
"GameListContextMenuCacheManagementPurgePptc": "清除 PPTC 缓存",
|
||||||
|
"GameListContextMenuCacheManagementPurgePptcToolTip": "删除游戏的 PPTC 缓存",
|
||||||
|
"GameListContextMenuCacheManagementPurgeShaderCache": "清除着色器缓存",
|
||||||
|
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "删除游戏的着色器缓存",
|
||||||
|
"GameListContextMenuCacheManagementOpenPptcDirectory": "打开 PPTC 目录",
|
||||||
|
"GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "打开包含游戏 PPTC 缓存的目录",
|
||||||
|
"GameListContextMenuCacheManagementOpenShaderCacheDirectory": "打开着色器缓存目录",
|
||||||
|
"GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip": "打开包含应用程序着色器缓存的目录",
|
||||||
|
"GameListContextMenuExtractData": "提取数据",
|
||||||
|
"GameListContextMenuExtractDataExeFS": "ExeFS",
|
||||||
|
"GameListContextMenuExtractDataExeFSToolTip": "从游戏的当前状态中提取 ExeFS 分区(包括更新)",
|
||||||
|
"GameListContextMenuExtractDataRomFS": "RomFS",
|
||||||
|
"GameListContextMenuExtractDataRomFSToolTip": "从游戏的当前状态中提取 RomFS 分区(包括更新)",
|
||||||
|
"GameListContextMenuExtractDataLogo": "图标",
|
||||||
|
"GameListContextMenuExtractDataLogoToolTip": "从游戏的当前状态中提取图标(包括更新)",
|
||||||
|
"StatusBarGamesLoaded": "{0}/{1} 游戏加载完成",
|
||||||
|
"StatusBarSystemVersion": "系统版本: {0}",
|
||||||
|
"Settings": "设置",
|
||||||
|
"SettingsTabGeneral": "用户界面",
|
||||||
|
"SettingsTabGeneralGeneral": "常规",
|
||||||
|
"SettingsTabGeneralEnableDiscordRichPresence": "启用Discord在线状态展示",
|
||||||
|
"SettingsTabGeneralCheckUpdatesOnLaunch": "自动检查更新",
|
||||||
|
"SettingsTabGeneralShowConfirmExitDialog": "显示 \"确认退出\" 对话框",
|
||||||
|
"SettingsTabGeneralHideCursorOnIdle": "空闲时隐藏鼠标",
|
||||||
|
"SettingsTabGeneralGameDirectories": "游戏目录",
|
||||||
|
"SettingsTabGeneralAdd": "添加",
|
||||||
|
"SettingsTabGeneralRemove": "删除",
|
||||||
|
"SettingsTabSystem": "系统",
|
||||||
|
"SettingsTabSystemCore": "核心",
|
||||||
|
"SettingsTabSystemSystemRegion": "系统区域:",
|
||||||
|
"SettingsTabSystemSystemRegionJapan": "日本",
|
||||||
|
"SettingsTabSystemSystemRegionUSA": "美国",
|
||||||
|
"SettingsTabSystemSystemRegionEurope": "欧洲",
|
||||||
|
"SettingsTabSystemSystemRegionAustralia": "澳大利亚",
|
||||||
|
"SettingsTabSystemSystemRegionChina": "中国",
|
||||||
|
"SettingsTabSystemSystemRegionKorea": "韩国",
|
||||||
|
"SettingsTabSystemSystemRegionTaiwan": "台湾地区",
|
||||||
|
"SettingsTabSystemSystemLanguage": "系统语言:",
|
||||||
|
"SettingsTabSystemSystemLanguageJapanese": "日语",
|
||||||
|
"SettingsTabSystemSystemLanguageAmericanEnglish": "美式英语",
|
||||||
|
"SettingsTabSystemSystemLanguageFrench": "法语",
|
||||||
|
"SettingsTabSystemSystemLanguageGerman": "德语",
|
||||||
|
"SettingsTabSystemSystemLanguageItalian": "意大利语",
|
||||||
|
"SettingsTabSystemSystemLanguageSpanish": "西班牙语",
|
||||||
|
"SettingsTabSystemSystemLanguageChinese": "中文(简体)",
|
||||||
|
"SettingsTabSystemSystemLanguageKorean": "韩语",
|
||||||
|
"SettingsTabSystemSystemLanguageDutch": "荷兰语",
|
||||||
|
"SettingsTabSystemSystemLanguagePortuguese": "葡萄牙语",
|
||||||
|
"SettingsTabSystemSystemLanguageRussian": "俄语",
|
||||||
|
"SettingsTabSystemSystemLanguageTaiwanese": "中文(繁体)",
|
||||||
|
"SettingsTabSystemSystemLanguageBritishEnglish": "英式英语",
|
||||||
|
"SettingsTabSystemSystemLanguageCanadianFrench": "加拿大法语",
|
||||||
|
"SettingsTabSystemSystemLanguageLatinAmericanSpanish": "拉美西班牙语",
|
||||||
|
"SettingsTabSystemSystemLanguageSimplifiedChinese": "简体中文(推荐)",
|
||||||
|
"SettingsTabSystemSystemLanguageTraditionalChinese": "繁体中文(推荐)",
|
||||||
|
"SettingsTabSystemSystemTimeZone": "系统时区:",
|
||||||
|
"SettingsTabSystemSystemTime": "系统时钟:",
|
||||||
|
"SettingsTabSystemEnableVsync": "开启 VSync",
|
||||||
|
"SettingsTabSystemEnablePptc": "开启 PPTC 缓存",
|
||||||
|
"SettingsTabSystemEnableFsIntegrityChecks": "开启文件系统完整性检查",
|
||||||
|
"SettingsTabSystemAudioBackend": "音频后端:",
|
||||||
|
"SettingsTabSystemAudioBackendDummy": "无",
|
||||||
|
"SettingsTabSystemAudioBackendOpenAL": "OpenAL",
|
||||||
|
"SettingsTabSystemAudioBackendSoundIO": "SoundIO",
|
||||||
|
"SettingsTabSystemAudioBackendSDL2": "SDL2",
|
||||||
|
"SettingsTabSystemHacks": "修正",
|
||||||
|
"SettingsTabSystemHacksNote": " - 会引起模拟器不稳定",
|
||||||
|
"SettingsTabSystemExpandDramSize": "将模拟RAM大小扩展至 6GB",
|
||||||
|
"SettingsTabSystemIgnoreMissingServices": "忽略缺少的服务",
|
||||||
|
"SettingsTabGraphics": "图像",
|
||||||
|
"SettingsTabGraphicsEnhancements": "增强",
|
||||||
|
"SettingsTabGraphicsEnableShaderCache": "启用着色器缓存",
|
||||||
|
"SettingsTabGraphicsAnisotropicFiltering": "各向异性过滤:",
|
||||||
|
"SettingsTabGraphicsAnisotropicFilteringAuto": "自动",
|
||||||
|
"SettingsTabGraphicsAnisotropicFiltering2x": "2x",
|
||||||
|
"SettingsTabGraphicsAnisotropicFiltering4x": "4x",
|
||||||
|
"SettingsTabGraphicsAnisotropicFiltering8x": "8x",
|
||||||
|
"SettingsTabGraphicsAnisotropicFiltering16x": "16x",
|
||||||
|
"SettingsTabGraphicsResolutionScale": "分辨率缩放:",
|
||||||
|
"SettingsTabGraphicsResolutionScaleCustom": "自定义 (不推荐)",
|
||||||
|
"SettingsTabGraphicsResolutionScaleNative": "原生 (720p/1080p)",
|
||||||
|
"SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)",
|
||||||
|
"SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)",
|
||||||
|
"SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p)",
|
||||||
|
"SettingsTabGraphicsAspectRatio": "宽高比:",
|
||||||
|
"SettingsTabGraphicsAspectRatio4x3": "4:3",
|
||||||
|
"SettingsTabGraphicsAspectRatio16x9": "16:9",
|
||||||
|
"SettingsTabGraphicsAspectRatio16x10": "16:10",
|
||||||
|
"SettingsTabGraphicsAspectRatio21x9": "21:9",
|
||||||
|
"SettingsTabGraphicsAspectRatio32x9": "32:9",
|
||||||
|
"SettingsTabGraphicsAspectRatioStretch": "拉伸至屏幕",
|
||||||
|
"SettingsTabGraphicsDeveloperOptions": "开发者选项",
|
||||||
|
"SettingsTabGraphicsShaderDumpPath": "图形着色器转储路径:",
|
||||||
|
"SettingsTabLogging": "日志",
|
||||||
|
"SettingsTabLoggingLogging": "日志",
|
||||||
|
"SettingsTabLoggingEnableLoggingToFile": "保存日志为文件",
|
||||||
|
"SettingsTabLoggingEnableStubLogs": "记录Stub",
|
||||||
|
"SettingsTabLoggingEnableInfoLogs": "记录Info",
|
||||||
|
"SettingsTabLoggingEnableWarningLogs": "记录Warning",
|
||||||
|
"SettingsTabLoggingEnableErrorLogs": "记录Error",
|
||||||
|
"SettingsTabLoggingEnableTraceLogs": "记录Trace",
|
||||||
|
"SettingsTabLoggingEnableGuestLogs": "记录Guest",
|
||||||
|
"SettingsTabLoggingEnableFsAccessLogs": "记录文件访问",
|
||||||
|
"SettingsTabLoggingFsGlobalAccessLogMode": "记录全局文件访问模式:",
|
||||||
|
"SettingsTabLoggingDeveloperOptions": "开发者选项 (警告: 会降低性能)",
|
||||||
|
"SettingsTabLoggingOpenglLogLevel": "OpenGL日志级别:",
|
||||||
|
"SettingsTabLoggingOpenglLogLevelNone": "无",
|
||||||
|
"SettingsTabLoggingOpenglLogLevelError": "错误",
|
||||||
|
"SettingsTabLoggingOpenglLogLevelPerformance": "减速",
|
||||||
|
"SettingsTabLoggingOpenglLogLevelAll": "全部",
|
||||||
|
"SettingsTabLoggingEnableDebugLogs": "启用调试日志",
|
||||||
|
"SettingsTabInput": "输入",
|
||||||
|
"SettingsTabInputEnableDockedMode": "主机模式",
|
||||||
|
"SettingsTabInputDirectKeyboardAccess": "直通键盘控制",
|
||||||
|
"SettingsButtonSave": "保存",
|
||||||
|
"SettingsButtonClose": "关闭",
|
||||||
|
"SettingsButtonApply": "应用",
|
||||||
|
"ControllerSettingsPlayer": "玩家",
|
||||||
|
"ControllerSettingsPlayer1": "玩家 1",
|
||||||
|
"ControllerSettingsPlayer2": "玩家 2",
|
||||||
|
"ControllerSettingsPlayer3": "玩家 3",
|
||||||
|
"ControllerSettingsPlayer4": "玩家 4",
|
||||||
|
"ControllerSettingsPlayer5": "玩家 5",
|
||||||
|
"ControllerSettingsPlayer6": "玩家 6",
|
||||||
|
"ControllerSettingsPlayer7": "玩家 7",
|
||||||
|
"ControllerSettingsPlayer8": "玩家 8",
|
||||||
|
"ControllerSettingsHandheld": "掌机模式",
|
||||||
|
"ControllerSettingsInputDevice": "输入设备",
|
||||||
|
"ControllerSettingsRefresh": "刷新",
|
||||||
|
"ControllerSettingsDeviceDisabled": "关闭",
|
||||||
|
"ControllerSettingsControllerType": "手柄类型",
|
||||||
|
"ControllerSettingsControllerTypeHandheld": "掌机",
|
||||||
|
"ControllerSettingsControllerTypeProController": "Pro手柄",
|
||||||
|
"ControllerSettingsControllerTypeJoyConPair": "JoyCon",
|
||||||
|
"ControllerSettingsControllerTypeJoyConLeft": "左JoyCon",
|
||||||
|
"ControllerSettingsControllerTypeJoyConRight": "右JoyCon",
|
||||||
|
"ControllerSettingsProfile": "预设",
|
||||||
|
"ControllerSettingsProfileDefault": "默认",
|
||||||
|
"ControllerSettingsLoad": "加载",
|
||||||
|
"ControllerSettingsAdd": "新建",
|
||||||
|
"ControllerSettingsRemove": "删除",
|
||||||
|
"ControllerSettingsButtons": "按钮",
|
||||||
|
"ControllerSettingsButtonA": "A",
|
||||||
|
"ControllerSettingsButtonB": "B",
|
||||||
|
"ControllerSettingsButtonX": "X",
|
||||||
|
"ControllerSettingsButtonY": "Y",
|
||||||
|
"ControllerSettingsButtonPlus": "+",
|
||||||
|
"ControllerSettingsButtonMinus": "-",
|
||||||
|
"ControllerSettingsDPad": "方向键",
|
||||||
|
"ControllerSettingsDPadUp": "上",
|
||||||
|
"ControllerSettingsDPadDown": "下",
|
||||||
|
"ControllerSettingsDPadLeft": "左",
|
||||||
|
"ControllerSettingsDPadRight": "右",
|
||||||
|
"ControllerSettingsLStick": "左摇杆",
|
||||||
|
"ControllerSettingsLStickButton": "按下",
|
||||||
|
"ControllerSettingsLStickUp": "上",
|
||||||
|
"ControllerSettingsLStickDown": "下",
|
||||||
|
"ControllerSettingsLStickLeft": "左",
|
||||||
|
"ControllerSettingsLStickRight": "右",
|
||||||
|
"ControllerSettingsLStickStick": "杆",
|
||||||
|
"ControllerSettingsLStickInvertXAxis": "反转 X 方向",
|
||||||
|
"ControllerSettingsLStickInvertYAxis": "反转 Y 方向",
|
||||||
|
"ControllerSettingsLStickDeadzone": "死区:",
|
||||||
|
"ControllerSettingsRStick": "右摇杆",
|
||||||
|
"ControllerSettingsRStickButton": "按下",
|
||||||
|
"ControllerSettingsRStickUp": "上",
|
||||||
|
"ControllerSettingsRStickDown": "下",
|
||||||
|
"ControllerSettingsRStickLeft": "左",
|
||||||
|
"ControllerSettingsRStickRight": "右",
|
||||||
|
"ControllerSettingsRStickStick": "杆",
|
||||||
|
"ControllerSettingsRStickInvertXAxis": "反转 X 方向",
|
||||||
|
"ControllerSettingsRStickInvertYAxis": "反转 Y 方向",
|
||||||
|
"ControllerSettingsRStickDeadzone": "死区:",
|
||||||
|
"ControllerSettingsTriggersLeft": "左扳机",
|
||||||
|
"ControllerSettingsTriggersRight": "右扳机",
|
||||||
|
"ControllerSettingsTriggersButtonsLeft": "左扳机键",
|
||||||
|
"ControllerSettingsTriggersButtonsRight": "右扳机键",
|
||||||
|
"ControllerSettingsTriggers": "扳机",
|
||||||
|
"ControllerSettingsTriggerL": "L",
|
||||||
|
"ControllerSettingsTriggerR": "R",
|
||||||
|
"ControllerSettingsTriggerZL": "ZL",
|
||||||
|
"ControllerSettingsTriggerZR": "ZR",
|
||||||
|
"ControllerSettingsLeftSL": "SL",
|
||||||
|
"ControllerSettingsLeftSR": "SR",
|
||||||
|
"ControllerSettingsRightSL": "SL",
|
||||||
|
"ControllerSettingsRightSR": "SR",
|
||||||
|
"ControllerSettingsExtraButtonsLeft": "左按键",
|
||||||
|
"ControllerSettingsExtraButtonsRight": "右按键",
|
||||||
|
"ControllerSettingsMisc": "其他",
|
||||||
|
"ControllerSettingsTriggerThreshold": "扳机阈值:",
|
||||||
|
"ControllerSettingsMotion": "体感",
|
||||||
|
"ControllerSettingsCemuHook": "CemuHook",
|
||||||
|
"ControllerSettingsMotionEnableMotionControls": "启用体感操作",
|
||||||
|
"ControllerSettingsMotionUseCemuhookCompatibleMotion": "使用CemuHook体感协议",
|
||||||
|
"ControllerSettingsMotionControllerSlot": "手柄:",
|
||||||
|
"ControllerSettingsMotionMirrorInput": "镜像操作",
|
||||||
|
"ControllerSettingsMotionRightJoyConSlot": "右JoyCon:",
|
||||||
|
"ControllerSettingsMotionServerHost": "服务器Host:",
|
||||||
|
"ControllerSettingsMotionGyroSensitivity": "陀螺仪敏感度:",
|
||||||
|
"ControllerSettingsMotionGyroDeadzone": "陀螺仪死区:",
|
||||||
|
"ControllerSettingsSave": "保存",
|
||||||
|
"ControllerSettingsClose": "关闭",
|
||||||
|
"UserProfilesSelectedUserProfile": "选择用户账户:",
|
||||||
|
"UserProfilesSaveProfileName": "保存账户名",
|
||||||
|
"UserProfilesChangeProfileImage": "更换头像",
|
||||||
|
"UserProfilesAvailableUserProfiles": "现有的账户:",
|
||||||
|
"UserProfilesAddNewProfile": "新建账户",
|
||||||
|
"UserProfilesDeleteSelectedProfile": "删除选择的账户",
|
||||||
|
"UserProfilesClose": "关闭",
|
||||||
|
"ProfileImageSelectionTitle": "头像选择",
|
||||||
|
"ProfileImageSelectionHeader": "选择合适的头像图片",
|
||||||
|
"ProfileImageSelectionNote": "您可以导入自定义头像,或从系统中选择头像",
|
||||||
|
"ProfileImageSelectionImportImage": "导入图像文件",
|
||||||
|
"ProfileImageSelectionSelectAvatar": "选择系统头像",
|
||||||
|
"InputDialogTitle": "输入对话框",
|
||||||
|
"InputDialogOk": "完成",
|
||||||
|
"InputDialogCancel": "取消",
|
||||||
|
"InputDialogAddNewProfileTitle": "选择用户名称",
|
||||||
|
"InputDialogAddNewProfileHeader": "请输入账户名称",
|
||||||
|
"InputDialogAddNewProfileSubtext": "(最大长度: {0})",
|
||||||
|
"AvatarChoose": "选择",
|
||||||
|
"AvatarSetBackgroundColor": "设置背景色",
|
||||||
|
"AvatarClose": "关闭",
|
||||||
|
"ControllerSettingsLoadProfileToolTip": "加载预设",
|
||||||
|
"ControllerSettingsAddProfileToolTip": "新增预设",
|
||||||
|
"ControllerSettingsRemoveProfileToolTip": "删除预设",
|
||||||
|
"ControllerSettingsSaveProfileToolTip": "保存预设",
|
||||||
|
"MenuBarFileToolsTakeScreenshot": "保存截图",
|
||||||
|
"MenuBarFileToolsHideUi": "隐藏UI",
|
||||||
|
"GameListContextMenuToggleFavorite": "标记为收藏",
|
||||||
|
"GameListContextMenuToggleFavoriteToolTip": "启用或取消收藏标记",
|
||||||
|
"SettingsTabGeneralTheme": "主题",
|
||||||
|
"SettingsTabGeneralThemeCustomTheme": "自选主题路径",
|
||||||
|
"SettingsTabGeneralThemeBaseStyle": "主题色调",
|
||||||
|
"SettingsTabGeneralThemeBaseStyleDark": "暗黑",
|
||||||
|
"SettingsTabGeneralThemeBaseStyleLight": "浅色",
|
||||||
|
"SettingsTabGeneralThemeEnableCustomTheme": "使用自选主题界面",
|
||||||
|
"ButtonBrowse": "浏览",
|
||||||
|
"ControllerSettingsMotionConfigureCemuHookSettings": "配置CemuHook体感",
|
||||||
|
"ControllerSettingsRumble": "震动",
|
||||||
|
"ControllerSettingsRumbleEnable": "启用震动",
|
||||||
|
"ControllerSettingsRumbleStrongMultiplier": "强震动调节",
|
||||||
|
"ControllerSettingsRumbleWeakMultiplier": "弱震动调节",
|
||||||
|
"DialogMessageSaveNotAvailableMessage": "没有{0} [{1:x16}]的游戏存档",
|
||||||
|
"DialogMessageSaveNotAvailableCreateSaveMessage": "是否创建该游戏的存档文件夹?",
|
||||||
|
"DialogConfirmationTitle": "Ryujinx - 设置",
|
||||||
|
"DialogUpdaterTitle": "Ryujinx - 更新",
|
||||||
|
"DialogErrorTitle": "Ryujinx - 错误",
|
||||||
|
"DialogWarningTitle": "Ryujinx - 警告",
|
||||||
|
"DialogExitTitle": "Ryujinx - 关闭",
|
||||||
|
"DialogErrorMessage": "Ryujinx遇到了错误",
|
||||||
|
"DialogExitMessage": "是否关闭Ryujinx?",
|
||||||
|
"DialogExitSubMessage": "所有未保存的进度会丢失!",
|
||||||
|
"DialogMessageCreateSaveErrorMessage": "创建特定的存档时出错: {0}",
|
||||||
|
"DialogMessageFindSaveErrorMessage": "查找特定的存档时出错: {0}",
|
||||||
|
"FolderDialogExtractTitle": "选择要解压到的文件夹",
|
||||||
|
"DialogNcaExtractionMessage": "提取{1}的{0}分区...",
|
||||||
|
"DialogNcaExtractionTitle": "Ryujinx - NCA分区提取",
|
||||||
|
"DialogNcaExtractionMainNcaNotFoundErrorMessage": "提取失败。所选文件中不含主NCA文件",
|
||||||
|
"DialogNcaExtractionCheckLogErrorMessage": "提取失败。请查看日志文件获取详情。",
|
||||||
|
"DialogNcaExtractionSuccessMessage": "提取成功。",
|
||||||
|
"DialogUpdaterConvertFailedMessage": "无法转换当前 Ryujinx 版本。",
|
||||||
|
"DialogUpdaterCancelUpdateMessage": "更新取消!",
|
||||||
|
"DialogUpdaterAlreadyOnLatestVersionMessage": "您使用的Ryujinx是最新版本。",
|
||||||
|
"DialogUpdaterFailedToGetVersionMessage": "尝试从 Github 获取版本信息时无效。可能由于 GitHub Actions 正在编译新版本。请过几分钟重试。",
|
||||||
|
"DialogUpdaterConvertFailedGithubMessage": "无法转换从 Github 接收到的 Ryujinx 版本转换。",
|
||||||
|
"DialogUpdaterDownloadingMessage": "下载新版本中...",
|
||||||
|
"DialogUpdaterExtractionMessage": "正在提取更新...",
|
||||||
|
"DialogUpdaterRenamingMessage": "正在删除旧文件...",
|
||||||
|
"DialogUpdaterAddingFilesMessage": "安装更新中...",
|
||||||
|
"DialogUpdaterCompleteMessage": "更新成功!",
|
||||||
|
"DialogUpdaterRestartMessage": "立即重启 Ryujinx 完成更新?",
|
||||||
|
"DialogUpdaterArchNotSupportedMessage": "您运行的系统架构不受支持!",
|
||||||
|
"DialogUpdaterArchNotSupportedSubMessage": "(仅支持 x64 系统)",
|
||||||
|
"DialogUpdaterNoInternetMessage": "没有连接到互联网",
|
||||||
|
"DialogUpdaterNoInternetSubMessage": "请确保互联网连接正常。",
|
||||||
|
"DialogUpdaterDirtyBuildMessage": "不能更新第三方版本的 Ryujinx!",
|
||||||
|
"DialogUpdaterDirtyBuildSubMessage": "如果希望使用受支持的版本,请您在 https://ryujinx.org/ 下载。",
|
||||||
|
"DialogRestartRequiredMessage": "需要重启模拟器",
|
||||||
|
"DialogThemeRestartMessage": "主题设置已保存。需要重新启动才能生效。",
|
||||||
|
"DialogThemeRestartSubMessage": "您是否要重启?",
|
||||||
|
"DialogFirmwareInstallEmbeddedMessage": "要安装游戏内置的固件吗?(固件 {0})",
|
||||||
|
"DialogFirmwareInstallEmbeddedSuccessMessage": "未找到已安装的固件,但 Ryujinx 可以从现有的游戏安装固件{0}.\\n模拟器现在可以运行。",
|
||||||
|
"DialogFirmwareNoFirmwareInstalledMessage": "未安装固件",
|
||||||
|
"DialogFirmwareInstalledMessage": "已安装固件{0}",
|
||||||
|
"DialogOpenSettingsWindowLabel": "打开设置窗口",
|
||||||
|
"DialogControllerAppletTitle": "控制器小窗口",
|
||||||
|
"DialogMessageDialogErrorExceptionMessage": "显示消息对话框时出错: {0}",
|
||||||
|
"DialogSoftwareKeyboardErrorExceptionMessage": "显示软件键盘时出错: {0}",
|
||||||
|
"DialogErrorAppletErrorExceptionMessage": "显示错误对话框时出错: {0}",
|
||||||
|
"DialogUserErrorDialogMessage": "{0}: {1}",
|
||||||
|
"DialogUserErrorDialogInfoMessage": "\n有关修复此错误的更多信息,可以遵循我们的设置指南。",
|
||||||
|
"DialogUserErrorDialogTitle": "Ryujinx错误 ({0})",
|
||||||
|
"DialogAmiiboApiTitle": "Amiibo API",
|
||||||
|
"DialogAmiiboApiFailFetchMessage": "从 API 获取信息时出错。",
|
||||||
|
"DialogAmiiboApiConnectErrorMessage": "无法连接到 Amiibo API 服务器。服务器可能已关闭,或者您没有网络连接。",
|
||||||
|
"DialogProfileInvalidProfileErrorMessage": "预设{0} 与当前输入配置系统不兼容。",
|
||||||
|
"DialogProfileDefaultProfileOverwriteErrorMessage": "默认预设无法被覆盖",
|
||||||
|
"DialogProfileDeleteProfileTitle": "删除预设",
|
||||||
|
"DialogProfileDeleteProfileMessage": "删除后不可恢复,确定吗?",
|
||||||
|
"DialogWarning": "警告",
|
||||||
|
"DialogPPTCDeletionMessage": "您即将删除:\n\n{0}的 PPTC 缓存\n\n确定吗?",
|
||||||
|
"DialogPPTCDeletionErrorMessage": "清除位于{0}的 PPTC 缓存时出错: {1}",
|
||||||
|
"DialogShaderDeletionMessage": "您即将删除:\n\n{0}的着色器缓存\n\n确定吗?",
|
||||||
|
"DialogShaderDeletionErrorMessage": "清除位于{0}的着色器缓存时出错: {1}",
|
||||||
|
"DialogRyujinxErrorMessage": "Ryujinx遇到错误",
|
||||||
|
"DialogInvalidTitleIdErrorMessage": "UI 错误:所选游戏没有有效的标题ID",
|
||||||
|
"DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "路径{0}找不到有效的系统固件。",
|
||||||
|
"DialogFirmwareInstallerFirmwareInstallTitle": "安装固件{0}",
|
||||||
|
"DialogFirmwareInstallerFirmwareInstallMessage": "将安装{0}版本的系统。",
|
||||||
|
"DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\n这将替换当前系统版本{0}。",
|
||||||
|
"DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\n\n确认进行?",
|
||||||
|
"DialogFirmwareInstallerFirmwareInstallWaitMessage": "安装固件中...",
|
||||||
|
"DialogFirmwareInstallerFirmwareInstallSuccessMessage": "成功安装系统版本{0}。",
|
||||||
|
"DialogUserProfileDeletionWarningMessage": "删除后将没有可选择的用户账户",
|
||||||
|
"DialogUserProfileDeletionConfirmMessage": "是否删除选择的账户",
|
||||||
|
"DialogControllerSettingsModifiedConfirmMessage": "目前的输入预设已更新",
|
||||||
|
"DialogControllerSettingsModifiedConfirmSubMessage": "要保存吗?",
|
||||||
|
"DialogDlcLoadNcaErrorMessage": "{0}. 错误的文件: {1}",
|
||||||
|
"DialogDlcNoDlcErrorMessage": "选择的文件不包含所选游戏的 DLC!",
|
||||||
|
"DialogPerformanceCheckLoggingEnabledMessage": "您启用了跟踪日志,仅供开发人员使用。",
|
||||||
|
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "为了获得最佳性能,建议禁用跟踪日志记录。您是否要立即禁用?",
|
||||||
|
"DialogPerformanceCheckShaderDumpEnabledMessage": "您启用了着色器转储,仅供开发人员使用。",
|
||||||
|
"DialogPerformanceCheckShaderDumpEnabledConfirmMessage": "为了获得最佳性能,建议禁用着色器转储。您是否要立即禁用?",
|
||||||
|
"DialogLoadAppGameAlreadyLoadedMessage": "当前已加载有游戏",
|
||||||
|
"DialogLoadAppGameAlreadyLoadedSubMessage": "请停止模拟或关闭程序,再启动另一个游戏。",
|
||||||
|
"DialogUpdateAddUpdateErrorMessage": "选择的文件不包含所选游戏的更新!",
|
||||||
|
"DialogSettingsBackendThreadingWarningTitle": "警告 - 后端多线程",
|
||||||
|
"DialogSettingsBackendThreadingWarningMessage": "改变此选项后必须重启 Ryujinx 才能生效。根据您的硬件,您开启该选项时,可能需要手动禁用驱动程序本身的GL多线程。",
|
||||||
|
"SettingsTabGraphicsFeaturesOptions": "功能",
|
||||||
|
"SettingsTabGraphicsBackendMultithreading": "后端多线程:",
|
||||||
|
"CommonAuto": "自动(推荐)",
|
||||||
|
"CommonOff": "关闭",
|
||||||
|
"CommonOn": "打开",
|
||||||
|
"InputDialogYes": "是",
|
||||||
|
"InputDialogNo": "否",
|
||||||
|
"DialogProfileInvalidProfileNameErrorMessage": "文件名包含无效字符,请重试。",
|
||||||
|
"MenuBarOptionsPauseEmulation": "暂停",
|
||||||
|
"MenuBarOptionsResumeEmulation": "继续",
|
||||||
|
"AboutUrlTooltipMessage": "在浏览器中打开Ryujinx的官网。",
|
||||||
|
"AboutDisclaimerMessage": "Ryujinx以任何方式都与Nintendo™以及任何商业伙伴没有关联",
|
||||||
|
"AboutAmiiboDisclaimerMessage": "我们的Amiibo模拟使用了\nAmiiboAPI (www.amiiboapi.com) ",
|
||||||
|
"AboutPatreonUrlTooltipMessage": "在浏览器中打开Ryujinx的Patreon赞助页。",
|
||||||
|
"AboutGithubUrlTooltipMessage": "在浏览器中打开Ryujinx的GitHub代码库。",
|
||||||
|
"AboutDiscordUrlTooltipMessage": "在浏览器中打开Ryujinx的Discord邀请链接。",
|
||||||
|
"AboutTwitterUrlTooltipMessage": "在浏览器中打开Ryujinx的Twitter主页。",
|
||||||
|
"AboutRyujinxAboutTitle": "关于:",
|
||||||
|
"AboutRyujinxAboutContent": "Ryujinx是Nintendo Switch™的模拟器.\n您可以在Patreon上支持Ryujinx。\n关注Twitter或者Discord可以获取模拟器最新动态。\n如果您对开发感兴趣,欢迎来GitHub和Discord加入我们!",
|
||||||
|
"AboutRyujinxMaintainersTitle": "由以下作者维护:",
|
||||||
|
"AboutRyujinxMaintainersContentTooltipMessage": "在浏览器中打开贡献者的网页",
|
||||||
|
"AboutRyujinxSupprtersTitle": "感谢Patreon的赞助者:",
|
||||||
|
"AmiiboSeriesLabel": "Amiibo系列",
|
||||||
|
"AmiiboCharacterLabel": "角色",
|
||||||
|
"AmiiboScanButtonLabel": "扫描",
|
||||||
|
"AmiiboOptionsShowAllLabel": "显示所有 Amiibo",
|
||||||
|
"AmiiboOptionsUsRandomTagLabel": "修正: 使用随机标记的Uuid",
|
||||||
|
"DlcManagerTableHeadingEnabledLabel": "启用",
|
||||||
|
"DlcManagerTableHeadingTitleIdLabel": "游戏ID",
|
||||||
|
"DlcManagerTableHeadingContainerPathLabel": "文件夹路径",
|
||||||
|
"DlcManagerTableHeadingFullPathLabel": "完整路径",
|
||||||
|
"DlcManagerRemoveAllButton": "全部删除",
|
||||||
|
"MenuBarOptionsChangeLanguage": "更改语言",
|
||||||
|
"CommonSort": "排序",
|
||||||
|
"CommonShowNames": "显示名称",
|
||||||
|
"CommonFavorite": "收藏",
|
||||||
|
"OrderAscending": "从小到大",
|
||||||
|
"OrderDescending": "从大到小",
|
||||||
|
"SettingsTabGraphicsFeatures": "额外功能",
|
||||||
|
"ErrorWindowTitle": "错误窗口",
|
||||||
|
"ToggleDiscordTooltip": "启用或关闭Discord详细在线状态展示",
|
||||||
|
"AddGameDirBoxTooltip": "输入要添加的游戏目录",
|
||||||
|
"AddGameDirTooltip": "添加游戏目录到列表中",
|
||||||
|
"RemoveGameDirTooltip": "移除选中的目录",
|
||||||
|
"CustomThemeCheckTooltip": "启用或关闭自定义主题",
|
||||||
|
"CustomThemePathTooltip": "自定义主题的目录",
|
||||||
|
"CustomThemeBrowseTooltip": "查找自定义主题",
|
||||||
|
"DockModeToggleTooltip": "是否开启Swith的主机模式",
|
||||||
|
"DirectKeyboardTooltip": "是否开启\"直连键盘访问(HID)支持\" (部分游戏可以使用您的键盘输入文字)",
|
||||||
|
"DirectMouseTooltip": "是否开启\"直连鼠标访问(HID)支持\" (部分游戏可以使用您的鼠标导航)",
|
||||||
|
"RegionTooltip": "更改系统区域",
|
||||||
|
"LanguageTooltip": "更改系统语言",
|
||||||
|
"TimezoneTooltip": "更改系统时区",
|
||||||
|
"TimeTooltip": "更改系统时钟",
|
||||||
|
"VSyncToggleTooltip": "开启可以消除帧撕裂,关闭可以提高性能",
|
||||||
|
"PptcToggleTooltip": "开启以后减少游戏启动时间",
|
||||||
|
"FsIntegrityToggleTooltip": "是否检查游戏文件内容的完整性",
|
||||||
|
"AudioBackendTooltip": "默认推荐SDL,但每种音频后端对各类游戏兼容性可能不同",
|
||||||
|
"MemoryManagerTooltip": "改变Switch内存映射到电脑内存的方式,会影响CPU性能消耗",
|
||||||
|
"MemoryManagerSoftwareTooltip": "使用软件内存页管理,最精确但是速度最慢",
|
||||||
|
"MemoryManagerHostTooltip": "直接映射内存页到电脑内存,JIT效率很高",
|
||||||
|
"MemoryManagerUnsafeTooltip": "直接映射内存页,但是不检查内存溢出,JIT效率最高。Ryujinx可以访问任何位置的内存,所以相对不安全。此模式下只应运行您信任的游戏或软件(即官方游戏)",
|
||||||
|
"DRamTooltip": "扩展模拟的Switch内存为6GB,某些高清纹理MOD或4K MOD需要此选项",
|
||||||
|
"IgnoreMissingServicesTooltip": "忽略某些未实现的系统服务,少部分游戏需要此选项才能启动",
|
||||||
|
"GraphicsBackendThreadingTooltip": "启用后端多线程",
|
||||||
|
"GalThreadingTooltip": "使用模拟器自带的多线程调度,减少着色器编译的卡顿,并提高驱动程序的性能(尤其是缺失多线程的AMD)。NVIDIA用户需要重启模拟器才能禁用驱动本身的多线程,否则您需手动执行禁用获得最佳性能",
|
||||||
|
"ShaderCacheToggleTooltip": "开启后缓存着色器到硬盘,减少画面卡顿",
|
||||||
|
"ResolutionScaleTooltip": "缩放渲染的分辨率",
|
||||||
|
"ResolutionScaleEntryTooltip": "尽量使用如1.5的浮点倍数。非整数的倍率易引起错误",
|
||||||
|
"AnisotropyTooltip": "各向异性过滤等级。能提高倾斜视角纹理的清晰度('自动'使用游戏默认指定的等级)",
|
||||||
|
"AspectRatioTooltip": "模拟器渲染窗口的宽高比",
|
||||||
|
"ShaderDumpPathTooltip": "转储图形着色器的路径",
|
||||||
|
"FileLogTooltip": "是否保存日志文件到硬盘",
|
||||||
|
"StubLogTooltip": "记录stub消息",
|
||||||
|
"InfoLogTooltip": "记录info消息",
|
||||||
|
"WarnLogTooltip": "记录warning消息",
|
||||||
|
"ErrorLogTooltip": "记录error消息",
|
||||||
|
"TraceLogTooltip": "记录trace消息",
|
||||||
|
"GuestLogTooltip": "记录guest消息",
|
||||||
|
"FileAccessLogTooltip": "记录文件访问消息",
|
||||||
|
"FSAccessLogModeTooltip": "记录FS访问消息,输出到控制台。可选的模式是0-3",
|
||||||
|
"DeveloperOptionTooltip": "使用请谨慎",
|
||||||
|
"OpenGlLogLevel": "需要打开适当的日志等级",
|
||||||
|
"DebugLogTooltip": "记录debug消息",
|
||||||
|
"LoadApplicationFileTooltip": "选择Switch格式的游戏并加载",
|
||||||
|
"LoadApplicationFolderTooltip": "选择一个解包后格式的Switch游戏并加载",
|
||||||
|
"OpenRyujinxFolderTooltip": "打开Ryujinx系统目录",
|
||||||
|
"OpenRyujinxLogsTooltip": "打开日志存放的目录",
|
||||||
|
"ExitTooltip": "关闭Ryujinx",
|
||||||
|
"OpenSettingsTooltip": "打开设置窗口",
|
||||||
|
"OpenProfileManagerTooltip": "打开用户账号管理器",
|
||||||
|
"StopEmulationTooltip": "停止运行当前游戏并回到选择界面",
|
||||||
|
"CheckUpdatesTooltip": "检查新版本Ryujinx",
|
||||||
|
"OpenAboutTooltip": "打开'关于'窗口",
|
||||||
|
"GridSize": "网格尺寸",
|
||||||
|
"GridSizeTooltip": "调整网格模式的大小",
|
||||||
|
"SettingsTabSystemSystemLanguageBrazilianPortuguese": "巴西葡萄牙语",
|
||||||
|
"AboutRyujinxContributorsButtonHeader": "查看所有参与者",
|
||||||
|
"SettingsTabSystemAudioVolume": "音量: ",
|
||||||
|
"AudioVolumeTooltip": "调节音量",
|
||||||
|
"SettingsTabSystemEnableInternetAccess": "启用网络连接",
|
||||||
|
"EnableInternetAccessTooltip": "开启互联网访问。此选项打开后,效果类似于Switch连接到互联网的状态。注意即使此选项关闭,应用程序也偶尔有可能连接到网络",
|
||||||
|
"GameListContextMenuManageCheatToolTip": "管理金手指",
|
||||||
|
"GameListContextMenuManageCheat": "管理金手指",
|
||||||
|
"ControllerSettingsStickRange": "范围",
|
||||||
|
"DialogStopEmulationTitle": "Ryujinx - 停止模拟",
|
||||||
|
"DialogStopEmulationMessage": "是否确定停止模拟?",
|
||||||
|
"SettingsTabCpu": "CPU",
|
||||||
|
"SettingsTabAudio": "音频",
|
||||||
|
"SettingsTabNetwork": "网络",
|
||||||
|
"SettingsTabNetworkConnection": "网络连接",
|
||||||
|
"[REMOVE]SettingsTabGraphicsFrameRate": "主机刷新率:",
|
||||||
|
"[REMOVE]SettingsTabGraphicsFrameRateTooltip": "设置主机刷新率。设为 0 可以取消帧率限制",
|
||||||
|
"SettingsTabCpuCache": "CPU 缓存",
|
||||||
|
"SettingsTabCpuMemory": "CPU 内存",
|
||||||
|
"DialogUpdaterFlatpakNotSupportedMessage": "请通过 FlatHub 更新 Ryujinx。",
|
||||||
|
"UpdaterDisabledWarningTitle": "更新已禁用!",
|
||||||
|
"GameListContextMenuOpenSdModsDirectory": "打开Atmosphere MOD目录",
|
||||||
|
"GameListContextMenuOpenSdModsDirectoryToolTip": "打开包含应用程序MOD的其他Atmosphere SD卡目录",
|
||||||
|
"ControllerSettingsRotate90": "顺时针旋转 90°",
|
||||||
|
"IconSize": "图标尺寸",
|
||||||
|
"IconSizeTooltip": "更改游戏图标大小",
|
||||||
|
"MenuBarOptionsShowConsole": "显示控制台",
|
||||||
|
"ShaderCachePurgeError": "清除着色器缓存时出错: {0}: {1}",
|
||||||
|
"UserErrorNoKeys": "找不到密钥",
|
||||||
|
"UserErrorNoFirmware": "找不到固件",
|
||||||
|
"UserErrorFirmwareParsingFailed": "固件解析错误",
|
||||||
|
"UserErrorApplicationNotFound": "找不到应用程序",
|
||||||
|
"UserErrorUnknown": "未知错误",
|
||||||
|
"UserErrorUndefined": "未定义错误",
|
||||||
|
"UserErrorNoKeysDescription": "Ryujinx找不到 'prod.keys' 文件",
|
||||||
|
"UserErrorNoFirmwareDescription": "Ryujinx找不到任何已安装的固件",
|
||||||
|
"UserErrorFirmwareParsingFailedDescription": "Ryujinx无法解密选择的固件。通常是由于过旧的密钥。",
|
||||||
|
"UserErrorApplicationNotFoundDescription": "Ryujinx在选中路径找不到有效的应用程序。",
|
||||||
|
"UserErrorUnknownDescription": "发生未知错误!",
|
||||||
|
"UserErrorUndefinedDescription": "发生了未定义错误!此类错误不应出现,请联系开发人员!",
|
||||||
|
"OpenSetupGuideMessage": "打开设置教程",
|
||||||
|
"NoUpdate": "没有新版",
|
||||||
|
"TitleUpdateVersionLabel": "版本 {0} - {1}",
|
||||||
|
"RyujinxInfo": "Ryujinx - 信息",
|
||||||
|
"RyujinxConfirm": "Ryujinx - 确认",
|
||||||
|
"FileDialogAllTypes": "全部类型",
|
||||||
|
"Never": "从不",
|
||||||
|
"SwkbdMinCharacters": "至少应为 {0} 个字长",
|
||||||
|
"SwkbdMinRangeCharacters": "必须为 {0}-{1} 个字长",
|
||||||
|
"SoftwareKeyboard": "软件键盘",
|
||||||
|
"DialogControllerAppletMessagePlayerRange": "游戏需要 {0} 个玩家()持有:\n\nTYPES: {1}\n\nPLAYERS: {2}\n\n{3}请打开设置界面,配置手柄;或者关闭窗口。",
|
||||||
|
"DialogControllerAppletMessage": "游戏需要刚好 {0} 个玩家()持有 with:\n\nTYPES: {1}\n\nPLAYERS: {2}\n\n{3}请打开设置界面,配置手柄;或者关闭窗口。",
|
||||||
|
"DialogControllerAppletDockModeSet": "现在处于主机模式,无法使用掌机操作方式\n\n",
|
||||||
|
"UpdaterRenaming": "正在删除旧文件...",
|
||||||
|
"UpdaterRenameFailed": "更新过程中无法重命名文件: {0}",
|
||||||
|
"UpdaterAddingFiles": "安装更新中...",
|
||||||
|
"UpdaterExtracting": "正在提取更新...",
|
||||||
|
"UpdaterDownloading": "下载新版本中...",
|
||||||
|
"Game": "游戏",
|
||||||
|
"Docked": "主机模式",
|
||||||
|
"Handheld": "掌机模式",
|
||||||
|
"ConnectionError": "连接错误。",
|
||||||
|
"AboutPageDeveloperListMore": "{0} 以及等人...",
|
||||||
|
"ApiError": "API错误。",
|
||||||
|
"LoadingHeading": "正在加载 {0}",
|
||||||
|
"CompilingPPTC": "编译PPTC中",
|
||||||
|
"CompilingShaders": "编译着色器中",
|
||||||
|
"AllKeyboards": "所有键盘",
|
||||||
|
"OpenFileDialogTitle": "选择支持的文件格式",
|
||||||
|
"OpenFolderDialogTitle": "选择一个包含解包游戏的文件夹",
|
||||||
|
"AllSupportedFormats": "全部支持的格式",
|
||||||
|
"RyujinxUpdater": "Ryujinx 更新程序"
|
||||||
|
}
|
@ -123,6 +123,7 @@
|
|||||||
<None Remove="Assets\Locales\pt_BR.json" />
|
<None Remove="Assets\Locales\pt_BR.json" />
|
||||||
<None Remove="Assets\Locales\ru_RU.json" />
|
<None Remove="Assets\Locales\ru_RU.json" />
|
||||||
<None Remove="Assets\Locales\tr_TR.json" />
|
<None Remove="Assets\Locales\tr_TR.json" />
|
||||||
|
<None Remove="Assets\Locales\zh_CN.json" />
|
||||||
<None Remove="Assets\Styles\Styles.xaml" />
|
<None Remove="Assets\Styles\Styles.xaml" />
|
||||||
<None Remove="Assets\Styles\BaseDark.xaml" />
|
<None Remove="Assets\Styles\BaseDark.xaml" />
|
||||||
<None Remove="Assets\Styles\BaseLight.xaml" />
|
<None Remove="Assets\Styles\BaseLight.xaml" />
|
||||||
@ -139,6 +140,7 @@
|
|||||||
<EmbeddedResource Include="Assets\Locales\pt_BR.json" />
|
<EmbeddedResource Include="Assets\Locales\pt_BR.json" />
|
||||||
<EmbeddedResource Include="Assets\Locales\ru_RU.json" />
|
<EmbeddedResource Include="Assets\Locales\ru_RU.json" />
|
||||||
<EmbeddedResource Include="Assets\Locales\tr_TR.json" />
|
<EmbeddedResource Include="Assets\Locales\tr_TR.json" />
|
||||||
|
<EmbeddedResource Include="Assets\Locales\zh_CN.json" />
|
||||||
<EmbeddedResource Include="Assets\Styles\Styles.xaml" />
|
<EmbeddedResource Include="Assets\Styles\Styles.xaml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -175,6 +175,10 @@
|
|||||||
Command="{ReflectionBinding ChangeLanguage}"
|
Command="{ReflectionBinding ChangeLanguage}"
|
||||||
CommandParameter="tr_TR"
|
CommandParameter="tr_TR"
|
||||||
Header="Turkish" />
|
Header="Turkish" />
|
||||||
|
<MenuItem
|
||||||
|
Command="{ReflectionBinding ChangeLanguage}"
|
||||||
|
CommandParameter="zh_CN"
|
||||||
|
Header="Simplified Chinese" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
|
12
Ryujinx.Common/Memory/Box.cs
Normal file
12
Ryujinx.Common/Memory/Box.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace Ryujinx.Common.Memory
|
||||||
|
{
|
||||||
|
public class Box<T> where T : unmanaged
|
||||||
|
{
|
||||||
|
public T Data;
|
||||||
|
|
||||||
|
public Box()
|
||||||
|
{
|
||||||
|
Data = new T();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -32,6 +32,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
private readonly GpuChannel _channel;
|
private readonly GpuChannel _channel;
|
||||||
private readonly TexturePoolCache _texturePoolCache;
|
private readonly TexturePoolCache _texturePoolCache;
|
||||||
|
|
||||||
|
private TexturePool _cachedTexturePool;
|
||||||
|
private SamplerPool _cachedSamplerPool;
|
||||||
|
|
||||||
private readonly TextureBindingInfo[][] _textureBindings;
|
private readonly TextureBindingInfo[][] _textureBindings;
|
||||||
private readonly TextureBindingInfo[][] _imageBindings;
|
private readonly TextureBindingInfo[][] _imageBindings;
|
||||||
|
|
||||||
@ -343,9 +346,14 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
? _texturePoolCache.FindOrCreate(_channel, texturePoolAddress, _texturePoolMaximumId)
|
? _texturePoolCache.FindOrCreate(_channel, texturePoolAddress, _texturePoolMaximumId)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
SamplerPool samplerPool = _samplerPool;
|
||||||
|
|
||||||
// Check if the texture pool has been modified since bindings were last committed.
|
// Check if the texture pool has been modified since bindings were last committed.
|
||||||
// If it wasn't, then it's possible to avoid looking up textures again when the handle remains the same.
|
// If it wasn't, then it's possible to avoid looking up textures again when the handle remains the same.
|
||||||
bool poolModified = false;
|
bool poolModified = _cachedTexturePool != texturePool || _cachedSamplerPool != samplerPool;
|
||||||
|
|
||||||
|
_cachedTexturePool = texturePool;
|
||||||
|
_cachedSamplerPool = samplerPool;
|
||||||
|
|
||||||
if (texturePool != null)
|
if (texturePool != null)
|
||||||
{
|
{
|
||||||
@ -358,9 +366,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_samplerPool != null)
|
if (samplerPool != null)
|
||||||
{
|
{
|
||||||
int samplerPoolSequence = _samplerPool.CheckModified();
|
int samplerPoolSequence = samplerPool.CheckModified();
|
||||||
|
|
||||||
if (_samplerPoolSequence != samplerPoolSequence)
|
if (_samplerPoolSequence != samplerPoolSequence)
|
||||||
{
|
{
|
||||||
@ -783,14 +791,24 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
// The shader translator has code to detect separate texture and sampler uses with a bindless texture,
|
// The shader translator has code to detect separate texture and sampler uses with a bindless texture,
|
||||||
// turn that into a regular texture access and produce those special handles with values on the higher 16 bits.
|
// turn that into a regular texture access and produce those special handles with values on the higher 16 bits.
|
||||||
if (handleType != TextureHandleType.CombinedSampler)
|
if (handleType != TextureHandleType.CombinedSampler)
|
||||||
|
{
|
||||||
|
int samplerHandle;
|
||||||
|
|
||||||
|
if (handleType != TextureHandleType.SeparateConstantSamplerHandle)
|
||||||
{
|
{
|
||||||
ulong samplerBufferAddress = _isCompute
|
ulong samplerBufferAddress = _isCompute
|
||||||
? _channel.BufferManager.GetComputeUniformBufferAddress(samplerBufferIndex)
|
? _channel.BufferManager.GetComputeUniformBufferAddress(samplerBufferIndex)
|
||||||
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, samplerBufferIndex);
|
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, samplerBufferIndex);
|
||||||
|
|
||||||
int samplerHandle = _channel.MemoryManager.Physical.Read<int>(samplerBufferAddress + (uint)samplerWordOffset * 4);
|
samplerHandle = _channel.MemoryManager.Physical.Read<int>(samplerBufferAddress + (uint)samplerWordOffset * 4);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
samplerHandle = samplerWordOffset;
|
||||||
|
}
|
||||||
|
|
||||||
if (handleType == TextureHandleType.SeparateSamplerId)
|
if (handleType == TextureHandleType.SeparateSamplerId ||
|
||||||
|
handleType == TextureHandleType.SeparateConstantSamplerHandle)
|
||||||
{
|
{
|
||||||
samplerHandle <<= 20;
|
samplerHandle <<= 20;
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
private const ushort FileFormatVersionMajor = 1;
|
private const ushort FileFormatVersionMajor = 1;
|
||||||
private const ushort FileFormatVersionMinor = 1;
|
private const ushort FileFormatVersionMinor = 1;
|
||||||
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
||||||
private const uint CodeGenVersion = 1;
|
private const uint CodeGenVersion = 3424;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
|
@ -237,7 +237,7 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
/// <returns>True if the coordinates are normalized, false otherwise</returns>
|
/// <returns>True if the coordinates are normalized, false otherwise</returns>
|
||||||
bool QueryTextureCoordNormalized(int handle, int cbufSlot = -1)
|
bool QueryTextureCoordNormalized(int handle, int cbufSlot = -1)
|
||||||
{
|
{
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -7,7 +7,8 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
{
|
{
|
||||||
CombinedSampler = 0, // Must be 0.
|
CombinedSampler = 0, // Must be 0.
|
||||||
SeparateSamplerHandle = 1,
|
SeparateSamplerHandle = 1,
|
||||||
SeparateSamplerId = 2
|
SeparateSamplerId = 2,
|
||||||
|
SeparateConstantSamplerHandle = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TextureHandle
|
public static class TextureHandle
|
||||||
@ -97,9 +98,19 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
// turn that into a regular texture access and produce those special handles with values on the higher 16 bits.
|
// turn that into a regular texture access and produce those special handles with values on the higher 16 bits.
|
||||||
if (handleType != TextureHandleType.CombinedSampler)
|
if (handleType != TextureHandleType.CombinedSampler)
|
||||||
{
|
{
|
||||||
int samplerHandle = cachedSamplerBuffer[samplerWordOffset];
|
int samplerHandle;
|
||||||
|
|
||||||
if (handleType == TextureHandleType.SeparateSamplerId)
|
if (handleType != TextureHandleType.SeparateConstantSamplerHandle)
|
||||||
|
{
|
||||||
|
samplerHandle = cachedSamplerBuffer[samplerWordOffset];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
samplerHandle = samplerWordOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handleType == TextureHandleType.SeparateSamplerId ||
|
||||||
|
handleType == TextureHandleType.SeparateConstantSamplerHandle)
|
||||||
{
|
{
|
||||||
samplerHandle <<= 20;
|
samplerHandle <<= 20;
|
||||||
}
|
}
|
||||||
|
@ -51,16 +51,32 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
Operand src0 = Utils.FindLastOperation(handleCombineOp.GetSource(0), block);
|
Operand src0 = Utils.FindLastOperation(handleCombineOp.GetSource(0), block);
|
||||||
Operand src1 = Utils.FindLastOperation(handleCombineOp.GetSource(1), block);
|
Operand src1 = Utils.FindLastOperation(handleCombineOp.GetSource(1), block);
|
||||||
|
|
||||||
|
// For cases where we have a constant, ensure that the constant is always
|
||||||
|
// the second operand.
|
||||||
|
// Since this is a commutative operation, both are fine,
|
||||||
|
// and having a "canonical" representation simplifies some checks below.
|
||||||
|
if (src0.Type == OperandType.Constant && src1.Type != OperandType.Constant)
|
||||||
|
{
|
||||||
|
Operand temp = src1;
|
||||||
|
src1 = src0;
|
||||||
|
src0 = temp;
|
||||||
|
}
|
||||||
|
|
||||||
TextureHandleType handleType = TextureHandleType.SeparateSamplerHandle;
|
TextureHandleType handleType = TextureHandleType.SeparateSamplerHandle;
|
||||||
|
|
||||||
// Try to match masked pattern:
|
// Try to match the following patterns:
|
||||||
|
// Masked pattern:
|
||||||
// - samplerHandle = samplerHandle & 0xFFF00000;
|
// - samplerHandle = samplerHandle & 0xFFF00000;
|
||||||
// - textureHandle = textureHandle & 0xFFFFF;
|
// - textureHandle = textureHandle & 0xFFFFF;
|
||||||
// - combinedHandle = samplerHandle | textureHandle;
|
// - combinedHandle = samplerHandle | textureHandle;
|
||||||
// where samplerHandle and textureHandle comes from a constant buffer, and shifted pattern:
|
// Where samplerHandle and textureHandle comes from a constant buffer.
|
||||||
|
// Shifted pattern:
|
||||||
// - samplerHandle = samplerId << 20;
|
// - samplerHandle = samplerId << 20;
|
||||||
// - combinedHandle = samplerHandle | textureHandle;
|
// - combinedHandle = samplerHandle | textureHandle;
|
||||||
// where samplerId and textureHandle comes from a constant buffer.
|
// Where samplerId and textureHandle comes from a constant buffer.
|
||||||
|
// Constant pattern:
|
||||||
|
// - combinedHandle = samplerHandleConstant | textureHandle;
|
||||||
|
// Where samplerHandleConstant is a constant value, and textureHandle comes from a constant buffer.
|
||||||
if (src0.AsgOp is Operation src0AsgOp)
|
if (src0.AsgOp is Operation src0AsgOp)
|
||||||
{
|
{
|
||||||
if (src1.AsgOp is Operation src1AsgOp &&
|
if (src1.AsgOp is Operation src1AsgOp &&
|
||||||
@ -104,12 +120,27 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
handleType = TextureHandleType.SeparateSamplerId;
|
handleType = TextureHandleType.SeparateSamplerId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (src1.Type == OperandType.Constant && (src1.Value & 0xfffff) == 0)
|
||||||
|
{
|
||||||
|
handleType = TextureHandleType.SeparateConstantSamplerHandle;
|
||||||
|
}
|
||||||
|
|
||||||
if (src0.Type != OperandType.ConstantBuffer || src1.Type != OperandType.ConstantBuffer)
|
if (src0.Type != OperandType.ConstantBuffer)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (handleType == TextureHandleType.SeparateConstantSamplerHandle)
|
||||||
|
{
|
||||||
|
SetHandle(
|
||||||
|
config,
|
||||||
|
texOp,
|
||||||
|
TextureHandle.PackOffsets(src0.GetCbufOffset(), ((src1.Value >> 20) & 0xfff), handleType),
|
||||||
|
TextureHandle.PackSlots(src0.GetCbufSlot(), 0),
|
||||||
|
rewriteSamplerType);
|
||||||
|
}
|
||||||
|
else if (src1.Type == OperandType.ConstantBuffer)
|
||||||
|
{
|
||||||
SetHandle(
|
SetHandle(
|
||||||
config,
|
config,
|
||||||
texOp,
|
texOp,
|
||||||
@ -117,6 +148,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
TextureHandle.PackSlots(src0.GetCbufSlot(), src1.GetCbufSlot()),
|
TextureHandle.PackSlots(src0.GetCbufSlot(), src1.GetCbufSlot()),
|
||||||
rewriteSamplerType);
|
rewriteSamplerType);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (texOp.Inst == Instruction.ImageLoad ||
|
else if (texOp.Inst == Instruction.ImageLoad ||
|
||||||
texOp.Inst == Instruction.ImageStore ||
|
texOp.Inst == Instruction.ImageStore ||
|
||||||
texOp.Inst == Instruction.ImageAtomic)
|
texOp.Inst == Instruction.ImageAtomic)
|
||||||
|
@ -6,13 +6,13 @@ namespace Ryujinx.HLE.HOS.Services.Ptm.Ts
|
|||||||
[Service("ts")]
|
[Service("ts")]
|
||||||
class IMeasurementServer : IpcService
|
class IMeasurementServer : IpcService
|
||||||
{
|
{
|
||||||
private const uint DefaultTemperature = 42000u;
|
private const uint DefaultTemperature = 42u;
|
||||||
|
|
||||||
public IMeasurementServer(ServiceCtx context) { }
|
public IMeasurementServer(ServiceCtx context) { }
|
||||||
|
|
||||||
[CommandHipc(3)]
|
[CommandHipc(1)]
|
||||||
// GetTemperatureMilliC(Location location) -> u32
|
// GetTemperature(Location location) -> u32
|
||||||
public ResultCode GetTemperatureMilliC(ServiceCtx context)
|
public ResultCode GetTemperature(ServiceCtx context)
|
||||||
{
|
{
|
||||||
Location location = (Location)context.RequestData.ReadByte();
|
Location location = (Location)context.RequestData.ReadByte();
|
||||||
|
|
||||||
@ -22,5 +22,18 @@ namespace Ryujinx.HLE.HOS.Services.Ptm.Ts
|
|||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandHipc(3)]
|
||||||
|
// GetTemperatureMilliC(Location location) -> u32
|
||||||
|
public ResultCode GetTemperatureMilliC(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Location location = (Location)context.RequestData.ReadByte();
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServicePtm, new { location });
|
||||||
|
|
||||||
|
context.ResponseData.Write(DefaultTemperature * 1000);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,7 +2,10 @@ using Ryujinx.Common.Logging;
|
|||||||
using Ryujinx.Cpu;
|
using Ryujinx.Cpu;
|
||||||
using Ryujinx.HLE.HOS.Services.Time.TimeZone;
|
using Ryujinx.HLE.HOS.Services.Time.TimeZone;
|
||||||
using Ryujinx.HLE.Utilities;
|
using Ryujinx.HLE.Utilities;
|
||||||
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Time.StaticService
|
namespace Ryujinx.HLE.HOS.Services.Time.StaticService
|
||||||
@ -100,15 +103,12 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
|
|||||||
|
|
||||||
string locationName = StringUtils.ReadInlinedAsciiString(context.RequestData, 0x24);
|
string locationName = StringUtils.ReadInlinedAsciiString(context.RequestData, 0x24);
|
||||||
|
|
||||||
ResultCode resultCode = _timeZoneContentManager.LoadTimeZoneRule(out TimeZoneRule rules, locationName);
|
using (WritableRegion region = context.Memory.GetWritableRegion(bufferPosition, Unsafe.SizeOf<TimeZoneRule>()))
|
||||||
|
|
||||||
// Write TimeZoneRule if success
|
|
||||||
if (resultCode == ResultCode.Success)
|
|
||||||
{
|
{
|
||||||
MemoryHelper.Write(context.Memory, bufferPosition, rules);
|
ref TimeZoneRule rules = ref MemoryMarshal.Cast<byte, TimeZoneRule>(region.Memory.Span)[0];
|
||||||
}
|
|
||||||
|
|
||||||
return resultCode;
|
return _timeZoneContentManager.LoadTimeZoneRule(ref rules, locationName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandHipc(100)]
|
[CommandHipc(100)]
|
||||||
|
@ -4,9 +4,12 @@ using Ryujinx.Cpu;
|
|||||||
using Ryujinx.HLE.HOS.Services.Time.Clock;
|
using Ryujinx.HLE.HOS.Services.Time.Clock;
|
||||||
using Ryujinx.HLE.HOS.Services.Time.TimeZone;
|
using Ryujinx.HLE.HOS.Services.Time.TimeZone;
|
||||||
using Ryujinx.HLE.Utilities;
|
using Ryujinx.HLE.Utilities;
|
||||||
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Time.StaticService
|
namespace Ryujinx.HLE.HOS.Services.Time.StaticService
|
||||||
{
|
{
|
||||||
@ -165,11 +168,11 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
|
|||||||
|
|
||||||
using (MemoryStream timeZoneBinaryStream = new MemoryStream(temp))
|
using (MemoryStream timeZoneBinaryStream = new MemoryStream(temp))
|
||||||
{
|
{
|
||||||
result = _timeZoneManager.ParseTimeZoneRuleBinary(out TimeZoneRule timeZoneRule, timeZoneBinaryStream);
|
using (WritableRegion region = context.Memory.GetWritableRegion(timeZoneRuleBufferPosition, Unsafe.SizeOf<TimeZoneRule>()))
|
||||||
|
|
||||||
if (result == ResultCode.Success)
|
|
||||||
{
|
{
|
||||||
MemoryHelper.Write(context.Memory, timeZoneRuleBufferPosition, timeZoneRule);
|
ref TimeZoneRule rule = ref MemoryMarshal.Cast<byte, TimeZoneRule>(region.Memory.Span)[0];
|
||||||
|
|
||||||
|
result = _timeZoneManager.ParseTimeZoneRuleBinary(ref rule, timeZoneBinaryStream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,9 +202,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
|
|||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeZoneRule rules = MemoryHelper.Read<TimeZoneRule>(context.Memory, bufferPosition);
|
ReadOnlySpan<TimeZoneRule> rules = MemoryMarshal.Cast<byte, TimeZoneRule>(context.Memory.GetSpan(bufferPosition, (int)bufferSize));
|
||||||
|
|
||||||
ResultCode resultCode = _timeZoneManager.ToCalendarTime(rules, posixTime, out CalendarInfo calendar);
|
ResultCode resultCode = _timeZoneManager.ToCalendarTime(in rules[0], posixTime, out CalendarInfo calendar);
|
||||||
|
|
||||||
if (resultCode == 0)
|
if (resultCode == 0)
|
||||||
{
|
{
|
||||||
@ -244,9 +247,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
|
|||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeZoneRule rules = MemoryHelper.Read<TimeZoneRule>(context.Memory, inBufferPosition);
|
ReadOnlySpan<TimeZoneRule> rules = MemoryMarshal.Cast<byte, TimeZoneRule>(context.Memory.GetSpan(inBufferPosition, (int)inBufferSize));
|
||||||
|
|
||||||
ResultCode resultCode = _timeZoneManager.ToPosixTime(rules, calendarTime, out long posixTime);
|
ResultCode resultCode = _timeZoneManager.ToPosixTime(in rules[0], calendarTime, out long posixTime);
|
||||||
|
|
||||||
if (resultCode == ResultCode.Success)
|
if (resultCode == ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.HLE.Utilities;
|
using Ryujinx.HLE.Utilities;
|
||||||
using System;
|
using System;
|
||||||
@ -38,7 +39,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
new int[] { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
|
new int[] { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
|
||||||
};
|
};
|
||||||
|
|
||||||
private const string TimeZoneDefaultRule = ",M4.1.0,M10.5.0";
|
private static readonly byte[] TimeZoneDefaultRule = Encoding.ASCII.GetBytes(",M4.1.0,M10.5.0");
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 0x4, Size = 0x10)]
|
[StructLayout(LayoutKind.Sequential, Pack = 0x4, Size = 0x10)]
|
||||||
private struct CalendarTimeInternal
|
private struct CalendarTimeInternal
|
||||||
@ -133,7 +134,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return (t1 - t0) == SecondsPerRepeat;
|
return (t1 - t0) == SecondsPerRepeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TimeTypeEquals(TimeZoneRule outRules, byte aIndex, byte bIndex)
|
private static bool TimeTypeEquals(in TimeZoneRule outRules, byte aIndex, byte bIndex)
|
||||||
{
|
{
|
||||||
if (aIndex < 0 || aIndex >= outRules.TypeCount || bIndex < 0 || bIndex >= outRules.TypeCount)
|
if (aIndex < 0 || aIndex >= outRules.TypeCount || bIndex < 0 || bIndex >= outRules.TypeCount)
|
||||||
{
|
{
|
||||||
@ -150,7 +151,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
StringUtils.CompareCStr(outRules.Chars[a.AbbreviationListIndex..], outRules.Chars[b.AbbreviationListIndex..]) == 0;
|
StringUtils.CompareCStr(outRules.Chars[a.AbbreviationListIndex..], outRules.Chars[b.AbbreviationListIndex..]) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int GetQZName(ReadOnlySpan<char> name, int namePosition, char delimiter)
|
private static int GetQZName(ReadOnlySpan<byte> name, int namePosition, char delimiter)
|
||||||
{
|
{
|
||||||
int i = namePosition;
|
int i = namePosition;
|
||||||
|
|
||||||
@ -162,13 +163,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int GetTZName(char[] name, int namePosition)
|
private static int GetTZName(ReadOnlySpan<byte> name, int namePosition)
|
||||||
{
|
{
|
||||||
int i = namePosition;
|
int i = namePosition;
|
||||||
|
|
||||||
char c;
|
char c;
|
||||||
|
|
||||||
while ((c = name[i]) != '\0' && !char.IsDigit(c) && c != ',' && c != '-' && c != '+')
|
while ((c = (char)name[i]) != '\0' && !char.IsDigit(c) && c != ',' && c != '-' && c != '+')
|
||||||
{
|
{
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@ -176,7 +177,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool GetNum(char[] name, ref int namePosition, out int num, int min, int max)
|
private static bool GetNum(ReadOnlySpan<byte> name, ref int namePosition, out int num, int min, int max)
|
||||||
{
|
{
|
||||||
num = 0;
|
num = 0;
|
||||||
|
|
||||||
@ -185,7 +186,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char c = name[namePosition];
|
char c = (char)name[namePosition];
|
||||||
|
|
||||||
if (!char.IsDigit(c))
|
if (!char.IsDigit(c))
|
||||||
{
|
{
|
||||||
@ -205,7 +206,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
c = name[namePosition];
|
c = (char)name[namePosition];
|
||||||
}
|
}
|
||||||
while (char.IsDigit(c));
|
while (char.IsDigit(c));
|
||||||
|
|
||||||
@ -217,7 +218,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool GetSeconds(char[] name, ref int namePosition, out int seconds)
|
private static bool GetSeconds(ReadOnlySpan<byte> name, ref int namePosition, out int seconds)
|
||||||
{
|
{
|
||||||
seconds = 0;
|
seconds = 0;
|
||||||
|
|
||||||
@ -266,7 +267,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool GetOffset(char[] name, ref int namePosition, ref int offset)
|
private static bool GetOffset(ReadOnlySpan<byte> name, ref int namePosition, ref int offset)
|
||||||
{
|
{
|
||||||
bool isNegative = false;
|
bool isNegative = false;
|
||||||
|
|
||||||
@ -304,7 +305,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool GetRule(char[] name, ref int namePosition, out Rule rule)
|
private static bool GetRule(ReadOnlySpan<byte> name, ref int namePosition, out Rule rule)
|
||||||
{
|
{
|
||||||
rule = new Rule();
|
rule = new Rule();
|
||||||
|
|
||||||
@ -347,7 +348,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
|
|
||||||
isValid = GetNum(name, ref namePosition, out rule.Day, 0, DaysPerWekk - 1);
|
isValid = GetNum(name, ref namePosition, out rule.Day, 0, DaysPerWekk - 1);
|
||||||
}
|
}
|
||||||
else if (char.IsDigit(name[namePosition]))
|
else if (char.IsDigit((char)name[namePosition]))
|
||||||
{
|
{
|
||||||
rule.Type = RuleType.DayOfYear;
|
rule.Type = RuleType.DayOfYear;
|
||||||
isValid = GetNum(name, ref namePosition, out rule.Day, 0, DaysPerLYear - 1);
|
isValid = GetNum(name, ref namePosition, out rule.Day, 0, DaysPerLYear - 1);
|
||||||
@ -385,19 +386,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool ParsePosixName(ReadOnlySpan<char> name, out TimeZoneRule outRules, bool lastDitch)
|
private static bool ParsePosixName(ReadOnlySpan<byte> name, ref TimeZoneRule outRules, bool lastDitch)
|
||||||
{
|
{
|
||||||
outRules = new TimeZoneRule
|
outRules = new TimeZoneRule();
|
||||||
{
|
|
||||||
Ats = new long[TzMaxTimes],
|
|
||||||
Types = new byte[TzMaxTimes],
|
|
||||||
Ttis = new TimeTypeInfo[TzMaxTypes],
|
|
||||||
Chars = new char[TzCharsArraySize]
|
|
||||||
};
|
|
||||||
|
|
||||||
int stdLen;
|
int stdLen;
|
||||||
|
|
||||||
ReadOnlySpan<char> stdName = name;
|
ReadOnlySpan<byte> stdName = name;
|
||||||
int namePosition = 0;
|
int namePosition = 0;
|
||||||
int stdOffset = 0;
|
int stdOffset = 0;
|
||||||
|
|
||||||
@ -428,7 +423,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
namePosition = GetTZName(name.ToArray(), namePosition);
|
namePosition = GetTZName(name, namePosition);
|
||||||
stdLen = namePosition;
|
stdLen = namePosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -449,7 +444,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
int destLen = 0;
|
int destLen = 0;
|
||||||
int dstOffset = 0;
|
int dstOffset = 0;
|
||||||
|
|
||||||
ReadOnlySpan<char> destName = name.Slice(namePosition);
|
ReadOnlySpan<byte> destName = name.Slice(namePosition);
|
||||||
|
|
||||||
if (TzCharsArraySize < charCount)
|
if (TzCharsArraySize < charCount)
|
||||||
{
|
{
|
||||||
@ -476,7 +471,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
destName = name.Slice(namePosition);
|
destName = name.Slice(namePosition);
|
||||||
namePosition = GetTZName(name.ToArray(), namePosition);
|
namePosition = GetTZName(name, namePosition);
|
||||||
destLen = namePosition;
|
destLen = namePosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -507,7 +502,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
|
|
||||||
if (name[namePosition] == '\0')
|
if (name[namePosition] == '\0')
|
||||||
{
|
{
|
||||||
name = TimeZoneDefaultRule.ToCharArray();
|
name = TimeZoneDefaultRule;
|
||||||
namePosition = 0;
|
namePosition = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -515,7 +510,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
{
|
{
|
||||||
namePosition++;
|
namePosition++;
|
||||||
|
|
||||||
bool IsRuleValid = GetRule(name.ToArray(), ref namePosition, out Rule start);
|
bool IsRuleValid = GetRule(name, ref namePosition, out Rule start);
|
||||||
if (!IsRuleValid)
|
if (!IsRuleValid)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -526,7 +521,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
IsRuleValid = GetRule(name.ToArray(), ref namePosition, out Rule end);
|
IsRuleValid = GetRule(name, ref namePosition, out Rule end);
|
||||||
if (!IsRuleValid)
|
if (!IsRuleValid)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -738,7 +733,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
}
|
}
|
||||||
|
|
||||||
charsPosition += stdLen;
|
charsPosition += stdLen;
|
||||||
outRules.Chars[charsPosition++] = '\0';
|
outRules.Chars[charsPosition++] = 0;
|
||||||
|
|
||||||
if (destLen != 0)
|
if (destLen != 0)
|
||||||
{
|
{
|
||||||
@ -746,7 +741,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
{
|
{
|
||||||
outRules.Chars[charsPosition + i] = destName[i];
|
outRules.Chars[charsPosition + i] = destName[i];
|
||||||
}
|
}
|
||||||
outRules.Chars[charsPosition + destLen] = '\0';
|
outRules.Chars[charsPosition + destLen] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -882,20 +877,14 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool ParsePosixName(string name, out TimeZoneRule outRules)
|
internal static bool ParsePosixName(string name, ref TimeZoneRule outRules)
|
||||||
{
|
{
|
||||||
return ParsePosixName(name.ToCharArray(), out outRules, false);
|
return ParsePosixName(Encoding.ASCII.GetBytes(name), ref outRules, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool ParseTimeZoneBinary(out TimeZoneRule outRules, Stream inputData)
|
internal static bool ParseTimeZoneBinary(ref TimeZoneRule outRules, Stream inputData)
|
||||||
{
|
{
|
||||||
outRules = new TimeZoneRule
|
outRules = new TimeZoneRule();
|
||||||
{
|
|
||||||
Ats = new long[TzMaxTimes],
|
|
||||||
Types = new byte[TzMaxTimes],
|
|
||||||
Ttis = new TimeTypeInfo[TzMaxTypes],
|
|
||||||
Chars = new char[TzCharsArraySize]
|
|
||||||
};
|
|
||||||
|
|
||||||
BinaryReader reader = new BinaryReader(inputData);
|
BinaryReader reader = new BinaryReader(inputData);
|
||||||
|
|
||||||
@ -1020,10 +1009,10 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
outRules.Ttis[i] = ttis;
|
outRules.Ttis[i] = ttis;
|
||||||
}
|
}
|
||||||
|
|
||||||
Encoding.ASCII.GetChars(p[..outRules.CharCount].ToArray()).CopyTo(outRules.Chars.AsSpan());
|
p[..outRules.CharCount].CopyTo(outRules.Chars);
|
||||||
|
|
||||||
p = p[outRules.CharCount..];
|
p = p[outRules.CharCount..];
|
||||||
outRules.Chars[outRules.CharCount] = '\0';
|
outRules.Chars[outRules.CharCount] = 0;
|
||||||
|
|
||||||
for (int i = 0; i < outRules.TypeCount; i++)
|
for (int i = 0; i < outRules.TypeCount; i++)
|
||||||
{
|
{
|
||||||
@ -1077,27 +1066,30 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
char[] tempName = new char[TzNameMax + 1];
|
byte[] tempName = new byte[TzNameMax + 1];
|
||||||
Array.Copy(workBuffer, position, tempName, 0, nRead);
|
Array.Copy(workBuffer, position, tempName, 0, nRead);
|
||||||
|
|
||||||
if (nRead > 2 && tempName[0] == '\n' && tempName[nRead - 1] == '\n' && outRules.TypeCount + 2 <= TzMaxTypes)
|
if (nRead > 2 && tempName[0] == '\n' && tempName[nRead - 1] == '\n' && outRules.TypeCount + 2 <= TzMaxTypes)
|
||||||
{
|
{
|
||||||
tempName[nRead - 1] = '\0';
|
tempName[nRead - 1] = 0;
|
||||||
|
|
||||||
char[] name = new char[TzNameMax];
|
byte[] name = new byte[TzNameMax];
|
||||||
Array.Copy(tempName, 1, name, 0, nRead - 1);
|
Array.Copy(tempName, 1, name, 0, nRead - 1);
|
||||||
|
|
||||||
if (ParsePosixName(name, out TimeZoneRule tempRules, false))
|
Box<TimeZoneRule> tempRulesBox = new Box<TimeZoneRule>();
|
||||||
|
ref TimeZoneRule tempRules = ref tempRulesBox.Data;
|
||||||
|
|
||||||
|
if (ParsePosixName(name, ref tempRulesBox.Data, false))
|
||||||
{
|
{
|
||||||
int abbreviationCount = 0;
|
int abbreviationCount = 0;
|
||||||
charCount = outRules.CharCount;
|
charCount = outRules.CharCount;
|
||||||
|
|
||||||
Span<char> chars = outRules.Chars;
|
Span<byte> chars = outRules.Chars;
|
||||||
|
|
||||||
for (int i = 0; i < tempRules.TypeCount; i++)
|
for (int i = 0; i < tempRules.TypeCount; i++)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<char> tempChars = tempRules.Chars;
|
ReadOnlySpan<byte> tempChars = tempRules.Chars;
|
||||||
ReadOnlySpan<char> tempAbbreviation = tempChars[tempRules.Ttis[i].AbbreviationListIndex..];
|
ReadOnlySpan<byte> tempAbbreviation = tempChars[tempRules.Ttis[i].AbbreviationListIndex..];
|
||||||
|
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
@ -1175,7 +1167,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
{
|
{
|
||||||
for (int i = 1; i < outRules.TimeCount; i++)
|
for (int i = 1; i < outRules.TimeCount; i++)
|
||||||
{
|
{
|
||||||
if (TimeTypeEquals(outRules, outRules.Types[i], outRules.Types[0]) && DifferByRepeat(outRules.Ats[i], outRules.Ats[0]))
|
if (TimeTypeEquals(in outRules, outRules.Types[i], outRules.Types[0]) && DifferByRepeat(outRules.Ats[i], outRules.Ats[0]))
|
||||||
{
|
{
|
||||||
outRules.GoBack = true;
|
outRules.GoBack = true;
|
||||||
break;
|
break;
|
||||||
@ -1184,7 +1176,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
|
|
||||||
for (int i = outRules.TimeCount - 2; i >= 0; i--)
|
for (int i = outRules.TimeCount - 2; i >= 0; i--)
|
||||||
{
|
{
|
||||||
if (TimeTypeEquals(outRules, outRules.Types[outRules.TimeCount - 1], outRules.Types[i]) && DifferByRepeat(outRules.Ats[outRules.TimeCount - 1], outRules.Ats[i]))
|
if (TimeTypeEquals(in outRules, outRules.Types[outRules.TimeCount - 1], outRules.Types[i]) && DifferByRepeat(outRules.Ats[outRules.TimeCount - 1], outRules.Ats[i]))
|
||||||
{
|
{
|
||||||
outRules.GoAhead = true;
|
outRules.GoAhead = true;
|
||||||
break;
|
break;
|
||||||
@ -1259,10 +1251,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
long remainingSeconds = time % SecondsPerDay;
|
long remainingSeconds = time % SecondsPerDay;
|
||||||
|
|
||||||
calendarTime = new CalendarTimeInternal();
|
calendarTime = new CalendarTimeInternal();
|
||||||
calendarAdditionalInfo = new CalendarAdditionalInfo()
|
calendarAdditionalInfo = new CalendarAdditionalInfo();
|
||||||
{
|
|
||||||
TimezoneName = new char[8]
|
|
||||||
};
|
|
||||||
|
|
||||||
while (timeDays < 0 || timeDays >= YearLengths[IsLeap((int)year)])
|
while (timeDays < 0 || timeDays >= YearLengths[IsLeap((int)year)])
|
||||||
{
|
{
|
||||||
@ -1353,13 +1342,10 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ResultCode ToCalendarTimeInternal(TimeZoneRule rules, long time, out CalendarTimeInternal calendarTime, out CalendarAdditionalInfo calendarAdditionalInfo)
|
private static ResultCode ToCalendarTimeInternal(in TimeZoneRule rules, long time, out CalendarTimeInternal calendarTime, out CalendarAdditionalInfo calendarAdditionalInfo)
|
||||||
{
|
{
|
||||||
calendarTime = new CalendarTimeInternal();
|
calendarTime = new CalendarTimeInternal();
|
||||||
calendarAdditionalInfo = new CalendarAdditionalInfo()
|
calendarAdditionalInfo = new CalendarAdditionalInfo();
|
||||||
{
|
|
||||||
TimezoneName = new char[8]
|
|
||||||
};
|
|
||||||
|
|
||||||
ResultCode result;
|
ResultCode result;
|
||||||
|
|
||||||
@ -1398,7 +1384,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return ResultCode.TimeNotFound;
|
return ResultCode.TimeNotFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = ToCalendarTimeInternal(rules, newTime, out calendarTime, out calendarAdditionalInfo);
|
result = ToCalendarTimeInternal(in rules, newTime, out calendarTime, out calendarAdditionalInfo);
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
@ -1450,17 +1436,17 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
{
|
{
|
||||||
calendarAdditionalInfo.IsDaySavingTime = rules.Ttis[ttiIndex].IsDaySavingTime;
|
calendarAdditionalInfo.IsDaySavingTime = rules.Ttis[ttiIndex].IsDaySavingTime;
|
||||||
|
|
||||||
ReadOnlySpan<char> timeZoneAbbreviation = rules.Chars.AsSpan()[rules.Ttis[ttiIndex].AbbreviationListIndex..];
|
ReadOnlySpan<byte> timeZoneAbbreviation = rules.Chars[rules.Ttis[ttiIndex].AbbreviationListIndex..];
|
||||||
|
|
||||||
int timeZoneSize = Math.Min(StringUtils.LengthCstr(timeZoneAbbreviation), 8);
|
int timeZoneSize = Math.Min(StringUtils.LengthCstr(timeZoneAbbreviation), 8);
|
||||||
|
|
||||||
timeZoneAbbreviation[..timeZoneSize].CopyTo(calendarAdditionalInfo.TimezoneName.AsSpan());
|
timeZoneAbbreviation[..timeZoneSize].CopyTo(calendarAdditionalInfo.TimezoneName.ToSpan());
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ResultCode ToPosixTimeInternal(TimeZoneRule rules, CalendarTimeInternal calendarTime, out long posixTime)
|
private static ResultCode ToPosixTimeInternal(in TimeZoneRule rules, CalendarTimeInternal calendarTime, out long posixTime)
|
||||||
{
|
{
|
||||||
posixTime = 0;
|
posixTime = 0;
|
||||||
|
|
||||||
@ -1604,7 +1590,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
|
|
||||||
int direction;
|
int direction;
|
||||||
|
|
||||||
ResultCode result = ToCalendarTimeInternal(rules, pivot, out CalendarTimeInternal candidateCalendarTime, out _);
|
ResultCode result = ToCalendarTimeInternal(in rules, pivot, out CalendarTimeInternal candidateCalendarTime, out _);
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
{
|
{
|
||||||
if (pivot > 0)
|
if (pivot > 0)
|
||||||
@ -1675,9 +1661,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static ResultCode ToCalendarTime(TimeZoneRule rules, long time, out CalendarInfo calendar)
|
internal static ResultCode ToCalendarTime(in TimeZoneRule rules, long time, out CalendarInfo calendar)
|
||||||
{
|
{
|
||||||
ResultCode result = ToCalendarTimeInternal(rules, time, out CalendarTimeInternal calendarTime, out CalendarAdditionalInfo calendarAdditionalInfo);
|
ResultCode result = ToCalendarTimeInternal(in rules, time, out CalendarTimeInternal calendarTime, out CalendarAdditionalInfo calendarAdditionalInfo);
|
||||||
|
|
||||||
calendar = new CalendarInfo()
|
calendar = new CalendarInfo()
|
||||||
{
|
{
|
||||||
@ -1697,7 +1683,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static ResultCode ToPosixTime(TimeZoneRule rules, CalendarTime calendarTime, out long posixTime)
|
internal static ResultCode ToPosixTime(in TimeZoneRule rules, CalendarTime calendarTime, out long posixTime)
|
||||||
{
|
{
|
||||||
CalendarTimeInternal calendarTimeInternal = new CalendarTimeInternal()
|
CalendarTimeInternal calendarTimeInternal = new CalendarTimeInternal()
|
||||||
{
|
{
|
||||||
@ -1710,7 +1696,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
Second = calendarTime.Second
|
Second = calendarTime.Second
|
||||||
};
|
};
|
||||||
|
|
||||||
return ToPosixTimeInternal(rules, calendarTimeInternal, out posixTime);
|
return ToPosixTimeInternal(in rules, calendarTimeInternal, out posixTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,8 +15,8 @@ using Ryujinx.HLE.Utilities;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
using static Ryujinx.HLE.HOS.Services.Time.TimeZone.TimeZoneRule;
|
using TimeZoneRuleBox = Ryujinx.Common.Memory.Box<Ryujinx.HLE.HOS.Services.Time.TimeZone.TimeZoneRule>;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
||||||
{
|
{
|
||||||
@ -149,7 +149,11 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeZone.ParseTimeZoneBinary(out TimeZoneRule tzRule, tzif.Get.AsStream());
|
TimeZoneRuleBox tzRuleBox = new TimeZoneRuleBox();
|
||||||
|
ref TimeZoneRule tzRule = ref tzRuleBox.Data;
|
||||||
|
|
||||||
|
TimeZone.ParseTimeZoneBinary(ref tzRule, tzif.Get.AsStream());
|
||||||
|
|
||||||
|
|
||||||
TimeTypeInfo ttInfo;
|
TimeTypeInfo ttInfo;
|
||||||
if (tzRule.TimeCount > 0) // Find the current transition period
|
if (tzRule.TimeCount > 0) // Find the current transition period
|
||||||
@ -174,10 +178,10 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var abbrStart = tzRule.Chars.AsSpan(ttInfo.AbbreviationListIndex);
|
var abbrStart = tzRule.Chars[ttInfo.AbbreviationListIndex..];
|
||||||
int abbrEnd = abbrStart.IndexOf('\0');
|
int abbrEnd = abbrStart.IndexOf((byte)0);
|
||||||
|
|
||||||
outList.Add((ttInfo.GmtOffset, locName, abbrStart.Slice(0, abbrEnd).ToString()));
|
outList.Add((ttInfo.GmtOffset, locName, Encoding.UTF8.GetString(abbrStart[..abbrEnd])));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,15 +280,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return (ResultCode)result.Value;
|
return (ResultCode)result.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal ResultCode LoadTimeZoneRule(out TimeZoneRule outRules, string locationName)
|
internal ResultCode LoadTimeZoneRule(ref TimeZoneRule rules, string locationName)
|
||||||
{
|
{
|
||||||
outRules = new TimeZoneRule
|
rules = default;
|
||||||
{
|
|
||||||
Ats = new long[TzMaxTimes],
|
|
||||||
Types = new byte[TzMaxTimes],
|
|
||||||
Ttis = new TimeTypeInfo[TzMaxTypes],
|
|
||||||
Chars = new char[TzCharsArraySize]
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!HasTimeZoneBinaryTitle())
|
if (!HasTimeZoneBinaryTitle())
|
||||||
{
|
{
|
||||||
@ -295,7 +293,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
|
|
||||||
if (result == ResultCode.Success)
|
if (result == ResultCode.Success)
|
||||||
{
|
{
|
||||||
result = Manager.ParseTimeZoneRuleBinary(out outRules, timeZoneBinaryStream);
|
result = Manager.ParseTimeZoneRuleBinary(ref rules, timeZoneBinaryStream);
|
||||||
|
|
||||||
ncaFile.Dispose();
|
ncaFile.Dispose();
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
using Ryujinx.HLE.HOS.Services.Time.Clock;
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Time.Clock;
|
||||||
using Ryujinx.HLE.Utilities;
|
using Ryujinx.HLE.Utilities;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using static Ryujinx.HLE.HOS.Services.Time.TimeZone.TimeZoneRule;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
||||||
{
|
{
|
||||||
class TimeZoneManager
|
class TimeZoneManager
|
||||||
{
|
{
|
||||||
private bool _isInitialized;
|
private bool _isInitialized;
|
||||||
private TimeZoneRule _myRules;
|
private Box<TimeZoneRule> _myRules;
|
||||||
private string _deviceLocationName;
|
private string _deviceLocationName;
|
||||||
private UInt128 _timeZoneRuleVersion;
|
private UInt128 _timeZoneRuleVersion;
|
||||||
private uint _totalLocationNameCount;
|
private uint _totalLocationNameCount;
|
||||||
@ -21,15 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
_deviceLocationName = "UTC";
|
_deviceLocationName = "UTC";
|
||||||
_timeZoneRuleVersion = new UInt128();
|
_timeZoneRuleVersion = new UInt128();
|
||||||
_lock = new object();
|
_lock = new object();
|
||||||
|
_myRules = new Box<TimeZoneRule>();
|
||||||
// Empty rules
|
|
||||||
_myRules = new TimeZoneRule
|
|
||||||
{
|
|
||||||
Ats = new long[TzMaxTimes],
|
|
||||||
Types = new byte[TzMaxTimes],
|
|
||||||
Ttis = new TimeTypeInfo[TzMaxTypes],
|
|
||||||
Chars = new char[TzCharsArraySize]
|
|
||||||
};
|
|
||||||
|
|
||||||
_timeZoneUpdateTimePoint = SteadyClockTimePoint.GetRandom();
|
_timeZoneUpdateTimePoint = SteadyClockTimePoint.GetRandom();
|
||||||
}
|
}
|
||||||
@ -78,7 +70,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
bool timeZoneConversionSuccess = TimeZone.ParseTimeZoneBinary(out TimeZoneRule rules, timeZoneBinaryStream);
|
Box<TimeZoneRule> rules = new Box<TimeZoneRule>();
|
||||||
|
|
||||||
|
bool timeZoneConversionSuccess = TimeZone.ParseTimeZoneBinary(ref rules.Data, timeZoneBinaryStream);
|
||||||
|
|
||||||
if (timeZoneConversionSuccess)
|
if (timeZoneConversionSuccess)
|
||||||
{
|
{
|
||||||
@ -154,13 +148,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResultCode ParseTimeZoneRuleBinary(out TimeZoneRule outRules, Stream timeZoneBinaryStream)
|
public ResultCode ParseTimeZoneRuleBinary(ref TimeZoneRule outRules, Stream timeZoneBinaryStream)
|
||||||
{
|
{
|
||||||
ResultCode result = ResultCode.Success;
|
ResultCode result = ResultCode.Success;
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
bool timeZoneConversionSuccess = TimeZone.ParseTimeZoneBinary(out outRules, timeZoneBinaryStream);
|
bool timeZoneConversionSuccess = TimeZone.ParseTimeZoneBinary(ref outRules, timeZoneBinaryStream);
|
||||||
|
|
||||||
if (!timeZoneConversionSuccess)
|
if (!timeZoneConversionSuccess)
|
||||||
{
|
{
|
||||||
@ -208,7 +202,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
{
|
{
|
||||||
if (_isInitialized)
|
if (_isInitialized)
|
||||||
{
|
{
|
||||||
result = ToCalendarTime(_myRules, time, out calendar);
|
result = ToCalendarTime(in _myRules.Data, time, out calendar);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -220,13 +214,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResultCode ToCalendarTime(TimeZoneRule rules, long time, out CalendarInfo calendar)
|
public ResultCode ToCalendarTime(in TimeZoneRule rules, long time, out CalendarInfo calendar)
|
||||||
{
|
{
|
||||||
ResultCode result;
|
ResultCode result;
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
result = TimeZone.ToCalendarTime(rules, time, out calendar);
|
result = TimeZone.ToCalendarTime(in rules, time, out calendar);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -240,7 +234,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
{
|
{
|
||||||
if (_isInitialized)
|
if (_isInitialized)
|
||||||
{
|
{
|
||||||
result = ToPosixTime(_myRules, calendarTime, out posixTime);
|
result = ToPosixTime(in _myRules.Data, calendarTime, out posixTime);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -252,13 +246,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResultCode ToPosixTime(TimeZoneRule rules, CalendarTime calendarTime, out long posixTime)
|
public ResultCode ToPosixTime(in TimeZoneRule rules, CalendarTime calendarTime, out long posixTime)
|
||||||
{
|
{
|
||||||
ResultCode result;
|
ResultCode result;
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
result = TimeZone.ToPosixTime(rules, calendarTime, out posixTime);
|
result = TimeZone.ToPosixTime(in rules, calendarTime, out posixTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Runtime.InteropServices;
|
using Ryujinx.Common.Memory;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
||||||
{
|
{
|
||||||
@ -8,14 +9,12 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
public uint DayOfWeek;
|
public uint DayOfWeek;
|
||||||
public uint DayOfYear;
|
public uint DayOfYear;
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
|
public Array8<byte> TimezoneName;
|
||||||
public char[] TimezoneName;
|
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.I1)]
|
[MarshalAs(UnmanagedType.I1)]
|
||||||
public bool IsDaySavingTime;
|
public bool IsDaySavingTime;
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
|
public Array3<byte> Padding;
|
||||||
public char[] Padding;
|
|
||||||
|
|
||||||
public int GmtOffset;
|
public int GmtOffset;
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,19 @@
|
|||||||
using System.Runtime.InteropServices;
|
using Ryujinx.Common.Memory;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 4)]
|
[StructLayout(LayoutKind.Sequential, Size = Size, Pack = 4)]
|
||||||
struct TimeTypeInfo
|
public struct TimeTypeInfo
|
||||||
{
|
{
|
||||||
|
public const int Size = 0x10;
|
||||||
|
|
||||||
public int GmtOffset;
|
public int GmtOffset;
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.I1)]
|
[MarshalAs(UnmanagedType.I1)]
|
||||||
public bool IsDaySavingTime;
|
public bool IsDaySavingTime;
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
|
public Array3<byte> Padding1;
|
||||||
public char[] Padding1;
|
|
||||||
|
|
||||||
public int AbbreviationListIndex;
|
public int AbbreviationListIndex;
|
||||||
|
|
||||||
@ -21,7 +23,6 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
[MarshalAs(UnmanagedType.I1)]
|
[MarshalAs(UnmanagedType.I1)]
|
||||||
public bool IsGMT;
|
public bool IsGMT;
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
|
public ushort Padding2;
|
||||||
public char[] Padding2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,9 +1,11 @@
|
|||||||
using System.Runtime.InteropServices;
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 0x4000, CharSet = CharSet.Ansi)]
|
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 0x4000, CharSet = CharSet.Ansi)]
|
||||||
struct TimeZoneRule
|
public struct TimeZoneRule
|
||||||
{
|
{
|
||||||
public const int TzMaxTypes = 128;
|
public const int TzMaxTypes = 128;
|
||||||
public const int TzMaxChars = 50;
|
public const int TzMaxChars = 50;
|
||||||
@ -22,17 +24,32 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
[MarshalAs(UnmanagedType.I1)]
|
[MarshalAs(UnmanagedType.I1)]
|
||||||
public bool GoAhead;
|
public bool GoAhead;
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = TzMaxTimes)]
|
[StructLayout(LayoutKind.Sequential, Size = sizeof(long) * TzMaxTimes)]
|
||||||
public long[] Ats;
|
private struct AtsStorageStruct { }
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = TzMaxTimes)]
|
private AtsStorageStruct _ats;
|
||||||
public byte[] Types;
|
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = TzMaxTypes)]
|
public Span<long> Ats => SpanHelpers.AsSpan<AtsStorageStruct, long>(ref _ats);
|
||||||
public TimeTypeInfo[] Ttis;
|
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = TzCharsArraySize)]
|
[StructLayout(LayoutKind.Sequential, Size = sizeof(byte) * TzMaxTimes)]
|
||||||
public char[] Chars;
|
private struct TypesStorageStruct { }
|
||||||
|
|
||||||
|
private TypesStorageStruct _types;
|
||||||
|
|
||||||
|
public Span<byte> Types => SpanHelpers.AsByteSpan(ref _types);
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = TimeTypeInfo.Size * TzMaxTypes)]
|
||||||
|
private struct TimeTypeInfoStorageStruct { }
|
||||||
|
|
||||||
|
private TimeTypeInfoStorageStruct _ttis;
|
||||||
|
|
||||||
|
public Span<TimeTypeInfo> Ttis => SpanHelpers.AsSpan<TimeTypeInfoStorageStruct, TimeTypeInfo>(ref _ttis);
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = sizeof(byte) * TzCharsArraySize)]
|
||||||
|
private struct CharsStorageStruct { }
|
||||||
|
|
||||||
|
private CharsStorageStruct _chars;
|
||||||
|
public Span<byte> Chars => SpanHelpers.AsByteSpan(ref _chars);
|
||||||
|
|
||||||
public int DefaultType;
|
public int DefaultType;
|
||||||
}
|
}
|
||||||
|
@ -128,7 +128,7 @@ namespace Ryujinx.HLE.Utilities
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int CompareCStr(ReadOnlySpan<char> s1, ReadOnlySpan<char> s2)
|
public static int CompareCStr(ReadOnlySpan<byte> s1, ReadOnlySpan<byte> s2)
|
||||||
{
|
{
|
||||||
int s1Index = 0;
|
int s1Index = 0;
|
||||||
int s2Index = 0;
|
int s2Index = 0;
|
||||||
@ -142,11 +142,11 @@ namespace Ryujinx.HLE.Utilities
|
|||||||
return s2[s2Index] - s1[s1Index];
|
return s2[s2Index] - s1[s1Index];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int LengthCstr(ReadOnlySpan<char> s)
|
public static int LengthCstr(ReadOnlySpan<byte> s)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
while (s[i] != '\0')
|
while (s[i] != 0)
|
||||||
{
|
{
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Cpu\Ryujinx.Cpu.csproj" />
|
<ProjectReference Include="..\Ryujinx.Cpu\Ryujinx.Cpu.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.HLE\Ryujinx.HLE.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Tests.Unicorn\Ryujinx.Tests.Unicorn.csproj" />
|
<ProjectReference Include="..\Ryujinx.Tests.Unicorn\Ryujinx.Tests.Unicorn.csproj" />
|
||||||
<ProjectReference Include="..\ARMeilleure\ARMeilleure.csproj" />
|
<ProjectReference Include="..\ARMeilleure\ARMeilleure.csproj" />
|
||||||
|
18
Ryujinx.Tests/Time/TimeZoneRuleTests.cs
Normal file
18
Ryujinx.Tests/Time/TimeZoneRuleTests.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using NUnit.Framework;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Time.TimeZone;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Tests.Time
|
||||||
|
{
|
||||||
|
internal class TimeZoneRuleTests
|
||||||
|
{
|
||||||
|
class EffectInfoParameterTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void EnsureTypeSize()
|
||||||
|
{
|
||||||
|
Assert.AreEqual(0x4000, Unsafe.SizeOf<TimeZoneRule>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user