Archived
1
0
Fork 0

add nintendo console errcode parser

This commit is contained in:
yzhh 2021-04-04 18:08:26 +08:00
parent c1fec8b5ea
commit 48628a7a57
7 changed files with 6742 additions and 0 deletions

View file

@ -0,0 +1,295 @@
#copied from kurisu(https://github.com/nh-server/Kurisu/tree/main/cogs/results)
import discord
from . import switch, wiiu_support, wiiu_results, ctr_support, ctr_results
from core.template import sendMessage
class ctx:
@classmethod
async def send(cls, kwargs, msg=False, embed=False):
def convertdict(ele: dict):
emsglst = []
if 'title' in ele:
emsglst.append(ele['title'])
if 'url' in ele:
emsglst.append(ele['url'])
if 'fields' in ele:
for field_value in ele['fields']:
emsglst.append(field_value['name'] + ': ' + field_value['value'])
if 'description' in ele:
emsglst.append(ele['description'])
if 'footer' in ele:
emsglst.append(ele['footer']['text'])
return emsglst
msglst = []
if msg:
if isinstance(msg, dict):
msgs = convertdict(msg)
msglst.append('\n'.join(msgs))
elif isinstance(msg, str):
msglst.append(msg)
if embed:
msgs = convertdict(embed)
msglst.append('\n'.join(msgs))
await sendMessage(kwargs, '\n'.join(msglst))
class Results:
"""
Parses game console result codes.
"""
def fetch(self, error):
if ctr_support.is_valid(error):
return ctr_support.get(error)
if ctr_results.is_valid(error):
return ctr_results.get(error)
if wiiu_support.is_valid(error):
return wiiu_support.get(error)
if wiiu_results.is_valid(error):
return wiiu_results.get(error)
if switch.is_valid(error):
return switch.get(error)
# Console name, module name, result, color
return None
def err2hex(self, error, suppress_error=False):
# If it's already hex, just return it.
if self.is_hex(error):
return error
# Only Switch is supported. The other two can only give nonsense results.
if switch.is_valid(error):
return switch.err2hex(error, suppress_error)
if not suppress_error:
return 'Invalid or unsupported error code format. \
Only Nintendo Switch XXXX-YYYY formatted error codes are supported.'
def hex2err(self, error, suppress_error=False):
# Don't bother processing anything if it's not hex.
if self.is_hex(error):
if switch.is_valid(error):
return switch.hex2err(error)
if not suppress_error:
return 'This isn\'t a hexadecimal value!'
def fixup_input(self, user_input):
# Truncate input to 16 chars so as not to create a huge embed or do
# eventual regex on a huge string. If we add support for consoles that
# that have longer error codes, adjust accordingly.
user_input = user_input[:16]
# Fix up hex input if 0x was omitted. It's fine if it doesn't convert.
try:
user_input = hex(int(user_input, 16))
except ValueError:
pass
return user_input
def is_hex(self, user_input):
try:
user_input = hex(int(user_input, 16))
except ValueError:
return False
return True
def check_meme(self, err: str) -> str:
memes = {
'0xdeadbeef': 'you sure you want to eat that?',
'0xdeadbabe': 'i think you have bigger problems if that\'s the case',
'0x8badf00d': 'told you not to eat it'
}
return memes.get(err.casefold())
async def result(self, kwargs):
"""
Displays information on game console result codes, with a fancy embed.
0x prefix is not required for hex input.
Examples:
.err 0xD960D02B
.err D960D02B
.err 022-2634
.err 102-2804
.err 2168-0002
.err 2-ARVHA-0000
"""
err = ' '.join(kwargs['trigger_msg'].split(' ')[1:])
err = self.fixup_input(err)
if (meme := self.check_meme(err)) is not None:
return await ctx.send(kwargs, meme)
ret = self.fetch(err)
if ret:
embed = discord.Embed(title=ret.get_title())
if ret.extra_description:
embed.description = ret.extra_description
for field in ret:
embed.add_field(name=field.field_name, value=field.message, inline=False)
embed.color = ret.color
embed = embed.to_dict()
await ctx.send(kwargs, embed=embed)
else:
await ctx.send(kwargs, f'The code you entered is \
invalid or is for a system I don\'t have support for.')
async def nxerr(self, kwargs):
"""
Displays information on switch result codes, with a fancy embed.
0x prefix is not required for hex input.
Examples:
.nxerr 0x4A8
.nxerr 4A8
.nxerr 2168-0002
.nxerr 2-ARVHA-0000
"""
err = ' '.join(kwargs['trigger_msg'].split(' ')[1:])
err = self.fixup_input(err)
if (meme := self.check_meme(err)) is not None:
return await ctx.send(kwargs, meme)
ret = None
if switch.is_valid(err):
ret = switch.get(err)
if ret:
embed = discord.Embed(title=ret.get_title())
if ret.extra_description:
embed.description = ret.extra_description
for field in ret:
embed.add_field(name=field.field_name, value=field.message, inline=False)
embed.color = ret.color
embed = embed.to_dict()
await ctx.send(kwargs, embed=embed)
else:
await ctx.send(kwargs, f'The code you entered is \
invalid for the switch.')
async def ctrerr(self, kwargs):
"""
Displays information on 3DS result codes, with a fancy embed.
0x prefix is not required for hex input.
Examples:
.ctrerr 0xD960D02B
.ctrerr D960D02B
.ctrerr 022-2634
"""
err = ' '.join(kwargs['trigger_msg'].split(' ')[1:])
if (meme := self.check_meme(err)) is not None:
return await ctx.send(kwargs, meme)
ret = None
if ctr_support.is_valid(err):
ret = ctr_support.get(err)
elif ctr_results.is_valid(err):
ret = ctr_results.get(err)
if ret:
embed = discord.Embed(title=ret.get_title())
if ret.extra_description:
embed.description = ret.extra_description
for field in ret:
embed.add_field(name=field.field_name, value=field.message, inline=False)
embed.color = ret.color
embed = embed.to_dict()
await ctx.send(kwargs, embed=embed)
else:
await ctx.send(kwargs, f'The code you entered is \
invalid for the 3DS.')
async def cafeerr(self, kwargs):
"""
Displays information on Wii U result codes, with a fancy embed.
0x prefix is not required for hex input.
Examples:
.cafeerr 0xC070FA80
.cafeerr C070FA80
.cafeerr 0x18106FFF
.cafeerr 18106FFF
.cafeerr 102-2804
"""
err = ' '.join(kwargs['trigger_msg'].split(' ')[1:])
err = self.fixup_input(err)
if (meme := self.check_meme(err)) is not None:
return await ctx.send(kwargs, meme)
ret = None
if wiiu_support.is_valid(err):
ret = wiiu_support.get(err)
elif wiiu_results.is_valid(err):
ret = wiiu_results.get(err)
if ret:
embed = discord.Embed(title=ret.get_title())
if ret.extra_description:
embed.description = ret.extra_description
for field in ret:
embed.add_field(name=field.field_name, value=field.message, inline=False)
embed.color = ret.color
embed = embed.to_dict()
await ctx.send(kwargs, embed=embed)
else:
await ctx.send(kwargs, f'The code you entered is \
invalid for the Wii U.')
async def cmderr2hex(self, kwargs):
"""
Converts a support code of a console to a hex result code.
Switch only supported.
3DS and WiiU support and result codes are not directly interchangeable.
"""
err = ' '.join(kwargs['trigger_msg'].split(' ')[1:])
error = self.fixup_input(err)
await ctx.send(kwargs, self.err2hex(error))
async def cmdhex2err(self, kwargs):
"""
Converts a hex result code of a console to a support code.
Switch only supported.
3DS and WiiU support and result codes are not directly interchangeable.
"""
err = ' '.join(kwargs['trigger_msg'].split(' ')[1:])
error = self.fixup_input(err)
await ctx.send(kwargs, self.hex2err(error))
async def hexinfo(self, kwargs):
"""
Breaks down a 3DS result code into its components.
"""
err = ' '.join(kwargs['trigger_msg'].split(' ')[1:])
error = self.fixup_input(err)
if self.is_hex(error):
if ctr_results.is_valid(error):
mod, desc, summary, level = ctr_results.hexinfo(error)
embed = discord.Embed(title="3DS hex result info")
embed.add_field(name="Module", value=mod, inline=False)
embed.add_field(name="Summary", value=summary, inline=False)
embed.add_field(name="Level", value=level, inline=False)
embed.add_field(name="Description", value=desc, inline=False)
embed = embed.to_dict()
await ctx.send(kwargs, embed=embed)
else:
await ctx.send(kwargs, 'This isn\'t a 3DS result code.')
else:
await ctx.send(kwargs, 'This isn\'t a hexadecimal value!')
command = {'err': Results().result}
help = {'err': {
'help': '~err <报错码> - 查询任天堂主机系列报错码详细信息。'}}

View file

@ -0,0 +1,353 @@
from .types import Module, ResultInfo, ConsoleErrorInfo, ConsoleErrorField
"""
This file contains all currently known 2DS/3DS result and error codes (hexadecimal).
There may be inaccuracies here; we'll do our best to correct them
when we find out more about them.
A result code is a 32-bit integer returned when calling various commands in the
3DS's operating system, Horizon. Its breaks down like so:
Bits | Description
-------------------
00-09 | Description
10-17 | Module
21-26 | Summary
27-31 | Level
Description: A value indicating exactly what happened.
Module: A value indicating who raised the error or returned the result.
Summary: A value indicating a shorter description of what happened.
Level: A value indicating the severity of the issue (fatal, temporary, etc.).
The 3DS makes it simple by providing all of these values directly. Other
consoles, such as the Wii U and Switch do not provide summaries or levels, so
those fields in the ResultInfo class are re-used for other similar purposes.
To add a module so the code understands it, simply add a new module number
to the 'modules' dictionary, with a Module variable as the value. If the module
has no known error codes, simply add a dummy Module instead (see the dict for
more info). See the various module variables for a more in-depth example
on how to make one.
Once you've added a module, or you want to add a new result code to an existing
module, add a new description value (for 3DS it's the 4 digit number after the dash)
as the key, and a ResultInfo variable with a text description of the error or result.
You can also add a second string to the ResultInfo to designate a support URL if
one exists. Not all results or errors have support webpages.
Simple example of adding a module with a sample result code:
test = Module('test', {
5: ResultInfo('test', 'https://example.com')
})
modules = {
9999: test
}
Sources used to compile these results and information:
https://www.3dbrew.org/wiki/Error_codes
Kurisu's previous err.py module
TODO: Add a number of result codes that were in the previous result code Kurisu
used. They were left out for the sake of getting this initial code done faster.
"""
common = Module('common', {
0: ResultInfo('Success'),
1000: ResultInfo('无效的选择'),
1001: ResultInfo('过大'),
1002: ResultInfo('未授权'),
1003: ResultInfo('已经完成'),
1004: ResultInfo('无效大小'),
1005: ResultInfo('无效枚举值'),
1006: ResultInfo('无效组合'),
1007: ResultInfo('无数据'),
1008: ResultInfo('忙碌'),
1009: ResultInfo('位址偏移'),
1010: ResultInfo('大小偏移'),
1011: ResultInfo('内存溢出'),
1012: ResultInfo('未生效'),
1013: ResultInfo('无效地址'),
1014: ResultInfo('无效指针'),
1015: ResultInfo('无效标头'),
1016: ResultInfo('未初始化'),
1017: ResultInfo('已初始化'),
1018: ResultInfo('未找到'),
1019: ResultInfo('请求取消'),
1020: ResultInfo('已存在'),
1021: ResultInfo('数组越界'),
1022: ResultInfo('超时'),
1023: ResultInfo('无效结果')
})
kernel = Module('kernel', {
2: ResultInfo('无效的内存权限。')
})
os = Module('os', {
10: ResultInfo('内存不足。'),
26: ResultInfo('远端关闭了会话。'),
47: ResultInfo('非法的命令标头。')
})
fs = Module('fs', {
101: ResultInfo('档案未挂载或挂载点未找到。'),
120: ResultInfo('应用或对象未找到。'),
141: ResultInfo('卡带未插入。'),
230: ResultInfo('非法的开启标识或权限。'),
391: ResultInfo('NCCH hash检查失败。'),
302: ResultInfo('RSA或AES-MAC校验失败。'),
395: ResultInfo('RomFS或Savedata hash检查失败。'),
630: ResultInfo('命令未被允许或缺失权限。'),
702: ResultInfo('无效路径。'),
761: ResultInfo('不正确的ExeFS读取大小。'),
(100, 179): ResultInfo('[媒体]未找到。'),
(180, 199): ResultInfo('已存在。'),
(200, 219): ResultInfo('空间不足。'),
(220, 229): ResultInfo('无效档案。'),
(230, 339): ResultInfo('不允许或写保护。'),
(360, 389): ResultInfo('格式错误。'),
(390, 399): ResultInfo('校验失败。'),
(600, 629): ResultInfo('资源溢出。'),
(630, 660): ResultInfo('权限不足。'),
(700, 729): ResultInfo('无效参数。'),
(730, 749): ResultInfo('未初始化。'),
(750, 759): ResultInfo('已初始化。'),
(760, 779): ResultInfo('不支持。')
})
srv = Module('srv', {
5: ResultInfo('无效的文字长度服务名称需要在0-8个字符之间'),
6: ResultInfo('访问服务权限不足(有一个程序的服务没有得到对应的权限)。'),
7: ResultInfo('文字长度不匹配内容(服务名称包含意料之外的未知字符)。')
})
nwm = Module('nwm', {
2: ResultInfo('这个错误经常在无线模块(快/已)坏掉时出现。')
})
am = Module('am', {
4: ResultInfo('非法的ticket版本。'),
32: ResultInfo('空CIA。'),
37: ResultInfo('无效的NCCH。'),
39: ResultInfo('无效的程序版本,'),
43: ResultInfo('数据库不存在或打开失败。'),
44: ResultInfo('尝试卸载系统程序。'),
106: ResultInfo('无效的签名/CIA。'),
393: ResultInfo('无效的数据库。'),
})
http = Module('http', {
105: ResultInfo('请求超时。')
})
nim = Module('nim', {
1: ResultInfo('无效的IPC字符串参数非null终止于其指示的长度'),
12: ResultInfo('CFG模块在读取配置的0xB0000时返回了无效的地区代码。'),
13: ResultInfo('CFG的SecureInfoGetSerialNo返回了零字符长度的序列号或“000000000000000”'),
18: ResultInfo('读取在系统存档里的NIM的.dat文件发生了错误数据损坏或数据长度不正确。'),
22: ResultInfo('任天堂服务器返回了无效的数据或无效的数据长度。(仅适用某些操作)'),
25: ResultInfo('IntegrityVerificationSeed正在等待服务器同步主机。如果未先通过IPC请求完成同步则无法于在线服务进行处理。'),
26: ResultInfo('任天堂服务器存有不可用/不允许的IntegrityVerificationSeed。可能在完成系统迁移后NIM向服务器请求导入IntegrityVerificationSeed时出现。'),
27: ResultInfo('CFG模块在读取配置的0xA0002时返回了无效的地区代码。'),
37: ResultInfo('服务处于备用模式。eShop封禁常规服务离线这于一服务器回应账户信息时出现。此账户和NNID不相关。'),
39: ResultInfo('HTTP状态码非200。仅适用某些操作'),
40: ResultInfo('处理自动传递XML时写入/读取错误。'),
41: ResultInfo('处理自动传递XML时写入/读取错误。Stubbed virtual call被调用'),
58: ResultInfo('CFG模块在读取配置的0xF0006时返回了无效的NPNS口令。'),
67: ResultInfo('当下载游戏的seed时得到了404状态码。'),
68: ResultInfo('当下载游戏的seed时得到了503状态码。')
})
mvd = Module('mvd', {
271: ResultInfo('无效的配置。')
})
qtm = Module('qtm', {
8: ResultInfo('相机正在工作或忙碌。')
})
# This is largely a dummy module, but FBI errors often get passed through the bot
# which return incorrect error strings. Since there's not really a feasible way to figure out the
# application which is throwing the error, this is the best compromise without giving the user
# false information.
application = Module('application-specific error', {
(0, 1023): ResultInfo('程序抛出了错误。请向其他人请教源代码的问题或者向程序的作者发送报告。')
})
# We have some modules partially documented, those that aren't have dummy Modules.
modules = {
0: common,
1: kernel,
2: Module('util'),
3: Module('file server'),
4: Module('loader server'),
5: Module('tcb'),
6: os,
7: Module('dbg'),
8: Module('dmnt'),
9: Module('pdn'),
10: Module('gsp'),
11: Module('i2c'),
12: Module('gpio'),
13: Module('dd'),
14: Module('codec'),
15: Module('spi'),
16: Module('pxi'),
17: fs,
18: Module('di'),
19: Module('hid'),
20: Module('cam'),
21: Module('pi'),
22: Module('pm'),
23: Module('pm_low'),
24: Module('fsi'),
25: srv,
26: Module('ndm'),
27: nwm,
28: Module('soc'),
29: Module('ldr'),
30: Module('acc'),
31: Module('romfs'),
32: am,
33: Module('hio'),
34: Module('updater'),
35: Module('mic'),
36: Module('fnd'),
37: Module('mp'),
38: Module('mpwl'),
39: Module('ac'),
40: http,
41: Module('dsp'),
42: Module('snd'),
43: Module('dlp'),
44: Module('hio_low'),
45: Module('csnd'),
46: Module('ssl'),
47: Module('am_low'),
48: Module('nex'),
49: Module('friends'),
50: Module('rdt'),
51: Module('applet'),
52: nim,
53: Module('ptm'),
54: Module('midi'),
55: Module('mc'),
56: Module('swc'),
57: Module('fatfs'),
58: Module('ngc'),
59: Module('card'),
60: Module('cardnor'),
61: Module('sdmc'),
62: Module('boss'),
63: Module('dbm'),
64: Module('config'),
65: Module('ps'),
66: Module('cec'),
67: Module('ir'),
68: Module('uds'),
69: Module('pl'),
70: Module('cup'),
71: Module('gyroscope'),
72: Module('mcu'),
73: Module('ns'),
74: Module('news'),
75: Module('ro'),
76: Module('gd'),
77: Module('card spi'),
78: Module('ec'),
79: Module('web browser'),
80: Module('test'),
81: Module('enc'),
82: Module('pia'),
83: Module('act'),
84: Module('vctl'),
85: Module('olv'),
86: Module('neia'),
87: Module('npns'),
90: Module('avd'),
91: Module('l2b'),
92: mvd,
93: Module('nfc'),
94: Module('uart'),
95: Module('spm'),
96: qtm,
97: Module('nfp'),
254: application,
}
levels = {
0: 'Success',
1: 'Info',
25: 'Status',
26: 'Temporary',
27: 'Permanent',
28: 'Usage',
29: 'Reinitialize',
30: 'Reset',
31: 'Fatal'
}
summaries = {
0: 'Success',
1: 'Nothing happened',
2: 'Would block',
3: 'Out of resource',
4: 'Not found',
5: 'Invalid state',
6: 'Not supported',
7: 'Invalid argument',
8: 'Wrong argument',
9: 'Canceled',
10: 'Status changed',
11: 'Internal',
63: 'Invalid result value'
}
CONSOLE_NAME = 'Nintendo 2DS/3DS'
# Suggested color to use if displaying information through a Discord bot's embed
COLOR = 0xCE181E
def is_valid(error: str):
try:
err_int = int(error, 16)
except ValueError:
return False
return True if err_int >= 0 and err_int.bit_length() <= 32 else False
def hexinfo(error: str):
error.strip()
err = int(error[2:], 16)
desc = err & 0x3FF
mod = (err >> 10) & 0xFF
summary = (err >> 21) & 0x3F
level = (err >> 27) & 0x1F
return mod, summary, level, desc
def construct_result(ret, mod, summary, level, desc):
module = modules.get(mod, Module(''))
ret.add_field(ConsoleErrorField('Module', message_str=module.name, supplementary_value=mod))
ret.add_field(ConsoleErrorField('Summary', message_str=summaries.get(summary, ''), supplementary_value=summary))
ret.add_field(ConsoleErrorField('Level', message_str=levels.get(level, ''), supplementary_value=level))
description = module.get_error(desc)
if description is None:
description = common.get_error(desc)
if description is None:
ret.add_field(ConsoleErrorField('Description', supplementary_value=desc))
else:
ret.add_field(ConsoleErrorField('Description', message_str=description.description, supplementary_value=desc))
else:
ret.add_field(ConsoleErrorField('Description', message_str=description.description, supplementary_value=desc))
return ret
def get(error: str):
ret = ConsoleErrorInfo(error, CONSOLE_NAME, COLOR)
mod, summary, level, desc = hexinfo(error)
return construct_result(ret, mod, summary, level, desc)

View file

@ -0,0 +1,342 @@
import re
from .ctr_results import modules as ctr_results_modules
from .types import Module, ResultInfo, ConsoleErrorInfo, ConsoleErrorField, \
BANNED_FIELD, WARNING_COLOR, UNKNOWN_CATEGORY_DESCRIPTION
"""
This file contains all currently known 2DS/3DS support codes.
There may be inaccuracies here; we'll do our best to correct them
when we find out more about them.
A "support" code, in contrast to a result code, is a human-readable string like
002-0102. They're meant to be more user-friendly than result codes, which are
typically integer values.
Note: the "modules" presented here are more like "categories". However, this difference
isn't enough to justify creating a different class with the same logic, so we'll just
refer to them as "modules" from now on.
To add a module/category so the code understands it, simply add a new module number
to the 'modules' dictionary, with a Module variable as the value. If the module
has no known error codes, simply add a dummy Module instead (see the dict for
more info). See the various module variables for a more in-depth example
on how to make one.
Once you've added a module, or you want to add a new support code to an existing
module, add a new description value (for 3DS it's the 4 digit number after the dash)
as the key, and a ResultInfo variable with a text description of the error or result.
You can also add a second string to the ResultInfo to designate a support URL if
one exists. Not all support codes have known error pages.
Simple example of adding a module with a sample support code:
test = Module('test', {
5: ResultInfo('test', 'https://example.com')
})
modules = {
9999: test
}
Sources used to compile this information:
Kurisu's previous err.py module
Ninendo's support knowledgebase at https://en-americas-support.nintendo.com/app/answers
"""
# 001: friends module, parental controls, online services in general?
friends = Module('friends', {
102: ResultInfo('此错误代表你从网络服务意外掉线。', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/17043'),
721: ResultInfo('此错误代表监护人已设置严格限制网络功能。', 'https://www.nintendo.com.au/help/3ds-error-codes')
})
# 002: bans and other account errors
account = Module('account', {
102: ResultInfo('此主机已被任天堂永久封禁。', is_ban=True),
107: ResultInfo('此主机已被任天堂暂时(?)封禁。', is_ban=True),
110: ResultInfo('当使用已放弃支持的youtube 3ds版时出现。'),
119: ResultInfo('需要系统更新。这个会在好友模块版本已过时时出现。'),
120: ResultInfo('游戏或应用需要更新。这个会在你启动的游戏或程序版本已过时时出现。'),
121: ResultInfo('Local friend code SEED的签证非法。这个应只在其被修改时出现。', is_ban=True),
123: ResultInfo('此主机已被任天堂永久封禁。', is_ban=True)
})
# 003: connection related errors
internet = Module('internet', {
299: ResultInfo('The Wireless Connection is currently deactivated. Please activate the wireless connection.'),
399: ResultInfo('Accepted EULA version is too low'),
1099: ResultInfo('Access point with given SSID not found.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/4249/kw/003-1099'),
2001: ResultInfo('DNS error. If you\'re using a custom DNS server, make sure the settings are correct.')
})
# Yet another nim hack. Why does this category have so many redundant errors?
NIM_4069 = ResultInfo('This error may appear if the eShop is unavailable. If the issue persists, you might need to replace your console\'s SD card.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/14413'),
# 005: nim
nim = Module('nim', {
# 005-2008 is the same as 007-2920 and 009-2920...
2008: ResultInfo('This error is typically displayed when a Nintendo eShop download failed, or when the title has an invalid ticket. Delete the title and/or its ticket in FBI and install it again from a legitimate source like the Nintendo eShop, or from your game cartridges if using cart dumps.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/41692'),
4040: ResultInfo('The connection timed out when connecting to the eShop.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/4429'),
4069: NIM_4069,
# in HTTP range...
4240: ResultInfo('This error code likely indicates a temporary service issue with the Nintendo eShop.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/28399'),
4305: ResultInfo('A generic error that may be displayed when the connection times out or you are unable to download software from the eShop.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/4346'),
4320: ResultInfo('A generic error that may be displayed when formatting your console or performing a system transfer.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/48382'),
5602: ResultInfo('Unable to connect to the eShop. This usually occurs when the System\'s region setting is incorrect. Change it in system settings and try connecting again.'),
5687: ResultInfo('A generic error that displays when you\'re unable to connect to the eShop.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/26251/'),
# in SOAP range...
5704: ResultInfo('A generic error that displays when you\'re unable to connect to the eShop.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/26252'),
5958: ResultInfo('Unknown eShop error. Usually seen on region-changed consoles.'),
5964: ResultInfo('Your NNID has been banned from accessing the eShop. You will need to contact Nintendo Support if you feel it was unjustified.'),
7545: NIM_4069,
7550: NIM_4069,
8025: NIM_4069,
8026: NIM_4069,
8029: NIM_4069
})
# 006: online matchmaking and gameplay errors
matchmaking = Module('matchmaking', {
112: ResultInfo('Typically displayed when an issue with connecting to Pokémon Bank occurs.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/4203/'),
(501, 502): ResultInfo('This may indicate in issue with the network being used blocking traffic necessary for online play.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/4204'),
612: ResultInfo('This error code generally indicates that your network is not optimal for peer to peer connections, likely due to your network\'s NAT type.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/25881'),
811: ResultInfo('This error code indicates the service you are attempting to use is currently unavailable due to ongoing maintenance.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/25910/'),
(800, 899): ResultInfo('These are typically shown when there is an error during the matchmaking process and you were unable to connect to the authentication servers.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/4328/')
})
# 007: errors related to (presumably) the eShop API
eshop_mint = Module('eshop (mint/api?)', {
200: ResultInfo('Could not access the SD card.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/4234'),
1221: ResultInfo('The download code you entered can only be redeemed within the relevant software title. It cannot be redeemed in Nintendo eShop.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/14600'),
2001: ResultInfo('Error when attempting to access eshop on a region changed console. Fixed by changing back to the console original region.'),
2100: ResultInfo('The connection to the Nintendo eShop timed out. This error code is often times caused by slow download times due to interference or a slow Internet connection.', 'See [the support page](https://en-americas-support.nintendo.com/app/answers/detail/a_id/4432) or [Nintendo\'s network status](https://support.nintendo.com/networkstatus).'),
2670: ResultInfo('An error occurred while attempting to connect.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/4383'),
2913: ResultInfo('The server is probably down. Try again later.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/10425'),
2916: ResultInfo('This is typically displayed when an error occurs while attempting to download a title from the Nintendo eShop.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/6557'),
2920: ResultInfo('This error is typically displayed when a Nintendo eShop download failed, or when the title has an invalid ticket. Delete the title and/or its ticket in FBI and install it again from a legitimate source like the Nintendo eShop, or from your game cartridges if using cart dumps.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/41692'),
2924: ResultInfo('Happens when opening eshop with a invalid language setting'),
3049: ResultInfo('The eShop is down for maintenance.', 'https://support.nintendo.com/networkstatus/'),
6106: ResultInfo('Occurs when attempting to re-download software from the eshop with an invalid or fake ticket')
})
# 009: errors related to (presumably) the eShop application itself
eshop_app = Module('eshop (app?)', {
# 1001: ResultInfo('The eShop is down for maintenance.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/45399'),
1000: ResultInfo('System update required (friends module?).'),
1001: eshop_mint.data[3049],
2705: ResultInfo('This error code is often the result of the Internet connection timing out or losing connection with the Nintendo eShop.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/14478'),
2913: ResultInfo('An eShop or in-game DLC download failed (or the server is down).', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/7243'),
2916: eshop_mint.data[2916],
2920: eshop_mint.data[2920],
2924: eshop_mint.data[2924],
2923: ResultInfo('You are unable to use a function which requires internet services, such as software updates or formatting the system memory.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/17014'),
2995: ResultInfo('This error can occur if the download code was entered incorrectly, has not yet been activated, has expired, was entered in the wrong place, or is intended for a region other than your own.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/13515'),
4079: ResultInfo('Unable to access SD card.'),
4998: ResultInfo('Local content is newer. Unknown what causes this.'),
6106: ResultInfo('AM error in NIM. Bad ticket is likely.'),
8401: ResultInfo('The update data is corrupted. Delete it and reinstall.')
})
# 011: eshop website, or other misc/overlapping errors
eshop_site = Module('eshop (website?)', {
3021: ResultInfo('Cannot find title on Nintendo eShop (incorrect region, or never existed?).'),
3136: ResultInfo('Nintendo eShop is currently unavailable. Try again later.'),
6901: ResultInfo('This console is permanently banned by Nintendo (displayed in Japanese for some reason).', is_ban=True)
})
# 014: system transfers?
data_transfer = Module('system transfer', {
13: ResultInfo('Attempting to do a system transfer with with a invalid language setting.'),
16: ResultInfo('Both consoles have the same movable.sed key. Format the target console and system transfer again.'),
62: ResultInfo('An error occurred during system transfer. Move closer to the wireless router and try again.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/15664')
})
# 012: a category related to the web browser or ssl module considered 1511
browser1 = Module('browser (?)', {
1004: ResultInfo('SSL connection failed.'),
1511: ResultInfo('Certificate warning.')
})
# 032: a second category related to the web browser
browser2 = Module('browser (?)', {
1820: ResultInfo('Displayed when the browser asks if you want to go to to a potentially dangerous website. Press \'yes\' to continue if you feel it is safe.')
})
# 022: more account stuff?
account2 = Module('account', {
2452: ResultInfo('Tried to access the eShop with UNITINFO patch enabled. Turn it off in Luma\'s options.'),
(2501, 2591): ResultInfo('NNID is already linked to another system. This can be the result of using System Transfer (where all NNIDs associated with the system are moved, whether they are currently linked or not), restoring the source console\'s NAND, and then attempting to use applications which require an NNID.'),
2511: ResultInfo('System update required (displayed by Miiverse?).'),
2613: ResultInfo('Incorrect email or password when attempting to link an existing NNID. Can also happen if the NNID is already linked to another system, or if you attempt to download an application from the eShop without a linked NNID on the console.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/4314/kw/022-2613'),
2631: ResultInfo('The NNID you are attempting to use has been deleted, or is unusable due to a System Transfer. A transferred NNID will only work on the target system.', 'https://en-americas-support.nintendo.com/app/answers/detail/a_id/4285/kw/022-2631'),
2633: ResultInfo('NNID is temporarily locked due to too many incorrect password attempts. Try again later.'),
2634: ResultInfo('NNID is not correctly linked on this console.', '[To fix it, follow these steps. Afterwards, reboot and sign into your NNID again.](https://3ds.hacks.guide/godmode9-usage#removing-an-nnid-without-formatting-your-device)'),
2812: ResultInfo('This console is permanently banned by Nintendo for playing Pokémon Sun & Moon online before the release date illegally.', is_ban=True),
2815: ResultInfo('This console is banned from accessing Miiverse by Nintendo.'),
5363: ResultInfo('Happens when trying to load NNID settings with a invalid language setting.'),
5515: ResultInfo('Network timeout.'),
})
# 090: application defined?
unknown1 = Module('unknown', {
212: ResultInfo('Game is permanently banned from Pokémon Global Link for using altered or illegal save data.', is_ban=True)
})
# We have some modules partially documented, those that aren't return None.
# These category names are largely made up based on the types of known errors they have,
# or based on their Wii U-equivalent, because Wii U is better documented.
modules = {
1: friends,
2: account,
3: internet,
5: nim,
6: matchmaking,
7: eshop_mint,
9: eshop_app,
11: eshop_site,
12: browser1,
14: data_transfer,
22: account2,
32: browser2,
90: unknown1
}
RE = re.compile(r'0\d{2}-\d{4}')
CONSOLE_NAME = 'Nintendo 2DS/3DS'
# Suggested color to use if displaying information through a Discord bot's embed
COLOR = 0xCE181E
def is_valid(error: str):
return RE.match(error)
def construct_result(ret, mod, desc):
module = ctr_results_modules.get(mod, Module(''))
ret.add_field(ConsoleErrorField('Module', message_str=module.name, supplementary_value=mod))
description = module.get_error(desc)
if description is None or not description.description:
description = ctr_results_modules[0].get_error(desc)
if description is None or not description.description:
ret.add_field(ConsoleErrorField('Description', supplementary_value=desc))
else:
ret.add_field(ConsoleErrorField('Description', message_str=description.description, supplementary_value=desc))
else:
ret.add_field(ConsoleErrorField('Description', message_str=description.description, supplementary_value=desc))
return ret
def construct_result_range(ret, mod, range_desc):
module = ctr_results_modules.get(mod, Module(''))
ret.add_field(ConsoleErrorField('Module', message_str=module.name, supplementary_value=mod))
found_descs = []
unknown_descs = []
for desc in range_desc:
if desc < 0 or desc > 1023:
continue
description = module.get_error(desc)
if description is None or not description.description:
description = ctr_results_modules[0].get_error(desc)
if description is None or not description.description:
unknown_descs.append(str(desc))
else:
found_descs.append(ConsoleErrorField('Description', message_str=description.description, supplementary_value=desc).message)
else:
found_descs.append(ConsoleErrorField('Description', message_str=description.description, supplementary_value=desc).message)
if found_descs:
ret.add_field(ConsoleErrorField('Possible known descriptions', message_str='\n'.join(found_descs)))
if unknown_descs:
ret.add_field(ConsoleErrorField('Possible unknown descriptions', message_str=', '.join(unknown_descs)))
return ret
def construct_support(ret, mod, desc):
category = modules.get(mod, Module(''))
if category.name:
ret.add_field(ConsoleErrorField('Category', message_str=category.name))
else:
ret.add_field(ConsoleErrorField('Category', supplementary_value=mod))
description = category.get_error(desc)
if description is not None and description.description:
ret.add_field(ConsoleErrorField('Description', message_str=description.description))
if description.support_url:
ret.add_field(ConsoleErrorField('Further information', message_str=description.support_url))
if description.is_ban:
ret.add_field(BANNED_FIELD)
ret.color = WARNING_COLOR
else:
ret.add_field(UNKNOWN_CATEGORY_DESCRIPTION)
return ret
def nim_handler(ret, description):
"""
Parses 3ds nim error codes in the following ranges:
005-2000 to 005-3023:
- NIM got a result of its own. Took description and added by 52000.
005-4200 to 005-4399:
- NIM got an HTTP result. Took description and added by 54200, cutting out at 54399 if it was beyond that.
005-4400 to 005-4999:
- Range of HTTP codes, however, can suffer collision.
005-5000 to 005-6999:
- SOAP Error Code range, when <ErrorCode> is not 0 on the SOAP responses.
005-7000 to 005-9999:
- Non specific expected results are formatted to an error code in nim by taking result module and shifting right by 5, and taking the result description and masked with 0x1F, then added both together along with 57000.
"""
# If we have a specific description for it in our knowledgebase,
# show it instead of doing the rest of the processing.
error = nim.get_error(description)
if error is not None and error.description:
return construct_support(ret, 5, description)
elif 2000 <= description < 3024:
description -= 2000
return construct_result(ret, 52, description) # nim result module, not support category
elif 4200 <= description < 4400:
description -= 4200
construct_result(ret, 40, description) # http result module, not support category
if description == 199:
ret.add_field(ConsoleErrorField('Extra note', message_str='Alternatively, any http description beyond 199.\nNIM truncates it to 199.'))
elif 4400 <= description < 5000:
description -= 4400
ret.add_field(ConsoleErrorField('Category', message_str='nim'))
if description < 100:
ret.add_field(ConsoleErrorField('HTTP Status Code', message_str=f'{description + 100}'))
elif 100 <= description < 500:
ret.add_field(ConsoleErrorField('HTTP Status Code', message_str=f'{description + 100} or {description} due to a programming mistake in NIM.'))
else:
ret.add_field(ConsoleErrorField('HTTP Status Code', message_str=f'{description}'))
elif 5000 <= description < 7000:
description -= 5000
ret.add_field(ConsoleErrorField('Category', message_str='nim'))
ret.add_field(ConsoleErrorField('Description', message_str=f'SOAP message returned result code {description} on a NIM operation.'))
# >= 7000 range is compacted
elif description >= 7000:
description -= 7000
module = description >> 5
# There are way more than 0x1F descriptions, but this is how Nintendo does it...
description = description & 0x1F
return construct_result_range(ret, module, range(0 + description, 1024 + description, 32))
else:
ret.add_field(ConsoleErrorField('Category', message_str='nim'))
ret.add_field(UNKNOWN_CATEGORY_DESCRIPTION)
return ret
def get(error: str):
mod = int(error[:3])
desc = int(error[4:])
ret = ConsoleErrorInfo(error, CONSOLE_NAME, COLOR)
if mod == 5: # 5 is NIM
return nim_handler(ret, desc)
return construct_support(ret, mod, desc)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,108 @@
class Module:
"""
Describes a Module. A Module contains a dictionary of ResultCodes,
and possibly a second dictionary with extra information.
A module itself is basically who raised the error or returned the result.
"""
def __init__(self, name, data={}, summaries={}):
self.name = name
self.data = data
self.summaries = summaries
def get_error(self, error: int):
value = self.data.get(error, None)
if value is not None:
return value
for key, value in self.data.items():
if isinstance(key, tuple) and key[0] <= error <= key[1]:
return value
return None
# If your modules require specific extra info for error ranges, add it here
def get_summary(self, summary: int):
value = self.summaries.get(summary, None)
if value is not None:
return value
for key, value in self.summaries.items():
if isinstance(key, tuple) and key[0] <= summary <= key[1]:
return value
return None
class ResultInfo:
"""
Holds information on a result or support code. A ResultInfo has a few fields which are used
to provide information about the result, error, or support code, including a support
webpage, if available.
"""
def __init__(self, description='', support_url='', is_ban=False):
self.description = description
self.support_url = support_url
self.is_ban = is_ban
class ConsoleErrorField:
def __init__(self, name: str, *, message_str: str = '', supplementary_value: int = None):
self.field_name = name
try:
self.message = message_str
except KeyboardInterrupt:
raise
except:
self.message = ''
if supplementary_value is None:
return
try:
supplementary_value = int(supplementary_value)
except ValueError:
return
self.message = f"{self.message} ({supplementary_value})" if self.message else f"{supplementary_value}"
class ConsoleErrorInfo:
"""
Holds the console name, the embed fields by an iteration of the parsed error or support code
"""
def __init__(self, error: str, console_name: str, color: int, extra_description: str = None, secondary_error: str = None):
self.error = error
self.secondary_error = secondary_error
self.console_name = console_name
self.color = color
self.fields = []
self.secondary_error = secondary_error
self.extra_description = extra_description
def __iter__(self):
return iter(self.fields)
def get_title(self):
if self.secondary_error:
return f"{self.error}/{self.secondary_error} ({self.console_name})"
else:
return f"{self.error} ({self.console_name})"
def add_field(self, field: ConsoleErrorField):
self.fields.append(field)
# Helper constants
REPORT_DETAILS = 'You should report relevant details to \
[the Kurisu repository](https://github.com/nh-server/Kurisu/issues).'
UNKNOWN_MODULE = ResultInfo(f'Invalid or unknown module. Are you sure you \
typed the error code in correctly? {REPORT_DETAILS}')
NO_RESULTS_FOUND = ResultInfo(f'I know about this module, but I don\'t have any \
information on error codes for it. {REPORT_DETAILS}')
BANNED_FIELD = ConsoleErrorField('Console, account and game bans', message_str='Nintendo Homebrew does not provide support \
for unbanning. Please do not ask for further assistance with this.')
WARNING_COLOR = 0xFFFF00
UNKNOWN_CATEGORY_DESCRIPTION = ConsoleErrorField('Description', message_str=f'Your support description appears to be unknown. {REPORT_DETAILS}')

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,510 @@
import re
from .types import Module, ResultInfo, ConsoleErrorInfo, ConsoleErrorField, \
BANNED_FIELD, WARNING_COLOR, UNKNOWN_CATEGORY_DESCRIPTION
"""
This file contains all currently known Wii U result and error codes.
There may be inaccuracies here; we'll do our best to correct them
when we find out more about them.
A "support" code, in contrast to a result code, is a human-readable string like
102-2811. They're meant to be more user-friendly than result codes, which are
typically integer values.
Note: the "modules" presented here are more like "categories". However, this difference
isn't enough to justify creating a different class with the same logic, so we'll just
refer to them as "modules" from now on.
To add a module so the code understands it, simply add a new module number
to the 'modules' dictionary, with a Module variable as the value. If the module
has no known error codes, simply add a dummy Module instead (see the dict for
more info). See the various module variables for a more in-depth example
on how to make one.
Once you've added a module, or you want to add a new result code to an existing
module, add a new description value (for Switch it's the final set of 4 digits after any dashes)
as the key, and a ResultInfo variable with a text description of the error or result.
You can also add a second string to the ResultInfo to designate a support URL if
one exists. Not all results or errors have support webpages.
Simple example of adding a module with a sample result code:
test = Module('test', {
5: ResultInfo('test', 'https://example.com')
})
modules = {
9999: test
}
Sources used to compile this list of results:
https://github.com/Kinnay/NintendoClients/wiki/Wii-U-Error-Codes
"""
fp = Module('fp (friends)', {
0: ResultInfo('Success.'),
1: ResultInfo('Session closed.'),
10: ResultInfo('Programming error.'),
11: ResultInfo('Not initialized.'),
12: ResultInfo('Already initialized.'),
13: ResultInfo('Invalid argument.'),
14: ResultInfo('Busy.'),
15: ResultInfo('Network clock is invalid.'),
16: ResultInfo('Not permitted.'),
100: ResultInfo('Undefined error.'),
101: ResultInfo('Reserved error 01.'),
102: ResultInfo('Unknown error.'),
103: ResultInfo('Not implemented.'),
104: ResultInfo('Invalid pointer.'),
105: ResultInfo('Operation aborted.'),
106: ResultInfo('Exception occurred.'),
107: ResultInfo('Access denied.'),
108: ResultInfo('Invalid handle.'),
109: ResultInfo('Invalid index.'),
110: ResultInfo('Out of memory.'),
111: ResultInfo('Invalid argument.'),
112: ResultInfo('Timeout.'),
113: ResultInfo('Initialization failure.'),
114: ResultInfo('Call initiation failure.'),
115: ResultInfo('Registration error.'),
116: ResultInfo('Buffer overflow.'),
117: ResultInfo('Invalid lock state.'),
200: ResultInfo('Undefined.'),
201: ResultInfo('Invalid signature.'),
202: ResultInfo('Incorrect version.'),
300: ResultInfo('Undefined.'),
301: ResultInfo('Connection failure.'),
302: ResultInfo('Not authenticated.'),
303: ResultInfo('Invalid username.'),
304: ResultInfo('Invalid password.'),
305: ResultInfo('Username already exists.'),
306: ResultInfo('Account is disabled.'),
307: ResultInfo('Account is expired.'),
308: ResultInfo('Concurrent login denied.'),
309: ResultInfo('Encryption failure.'),
310: ResultInfo('Invalid PID.'),
311: ResultInfo('Max connections reached.'),
312: ResultInfo('Invalid GID.'),
313: ResultInfo('Invalid thread ID.'),
314: ResultInfo('Invalid operation in live environment.'),
315: ResultInfo('Duplicate entry.'),
316: ResultInfo('Control script failure.'),
317: ResultInfo('Class not found.'),
318: ResultInfo('Reserved 18.'),
319: ResultInfo('Reserved 19.'),
320: ResultInfo('DDL mismatch.'),
321: ResultInfo('Reserved 21.'),
322: ResultInfo('Reserved 22.'),
400: ResultInfo('Undefined error.'),
401: ResultInfo('Exception occurred.'),
402: ResultInfo('Type error.'),
403: ResultInfo('Index error.'),
404: ResultInfo('Invalid reference.'),
405: ResultInfo('Call failure.'),
406: ResultInfo('Memory error.'),
407: ResultInfo('Operation error.'),
408: ResultInfo('Conversion error.'),
409: ResultInfo('Validation error.'),
500: ResultInfo('Undefined error.'),
501: ResultInfo('Unknown error.'),
502: ResultInfo('Connection failure.'),
503: ResultInfo('Invalid URL.'),
504: ResultInfo('Invalid key.'),
505: ResultInfo('Invalid URL type.'),
506: ResultInfo('Duplicate endpoint.'),
507: ResultInfo('I/O error.'),
508: ResultInfo('Timeout.'),
509: ResultInfo('Connection reset.'),
510: ResultInfo('Incorrect remote authentication.'),
511: ResultInfo('Server request error.'),
512: ResultInfo('Decompression failure.'),
513: ResultInfo('Congested end-point.'),
514: ResultInfo('Reserved 14.'),
515: ResultInfo('Reserved 15.'),
516: ResultInfo('Reserved 16.'),
517: ResultInfo('Reserved 17.'),
518: ResultInfo('Socket send warning.'),
519: ResultInfo('Unsupported NAT.'),
520: ResultInfo('DNS error.'),
521: ResultInfo('Proxy error.'),
522: ResultInfo('Data remaining.'),
523: ResultInfo('No buffer.'),
524: ResultInfo('Not found.'),
600: ResultInfo('Undefined error.'),
700: ResultInfo('Undefined error.'),
701: ResultInfo('Reserved 1.'),
702: ResultInfo('Not initialized.'),
703: ResultInfo('Already initialized.'),
704: ResultInfo('Not connected.'),
705: ResultInfo('Connected.'),
706: ResultInfo('Initialization failure.'),
707: ResultInfo('Out of memory.'),
708: ResultInfo('RMC failed.'),
709: ResultInfo('Invalid argument.'),
710: ResultInfo('Reserved 10.'),
711: ResultInfo('Invalid principal ID.'),
712: ResultInfo('Reserved 12.'),
713: ResultInfo('Reserved 13.'),
714: ResultInfo('Reserved 14.'),
715: ResultInfo('Reserved 15.'),
716: ResultInfo('Reserved 16.'),
717: ResultInfo('Reserved 17.'),
718: ResultInfo('Reserved 18.'),
719: ResultInfo('Reserved 19.'),
720: ResultInfo('File I/O error.'),
721: ResultInfo('P2P internet prohibited.'),
722: ResultInfo('Unknown error.'),
723: ResultInfo('Invalid state.'),
724: ResultInfo('Reservd 24.'),
725: ResultInfo('Adding a friend is prohibited.'),
726: ResultInfo('Reserved 26.'),
727: ResultInfo('Invalid account.'),
728: ResultInfo('Blacklisted by me.'),
729: ResultInfo('Reserved 29.'),
730: ResultInfo('Friend already added.'),
731: ResultInfo('Friend list limit exceeded.'),
732: ResultInfo('Requests limit exceeded.'),
733: ResultInfo('Invalid message ID.'),
734: ResultInfo('Message is not mine.'),
735: ResultInfo('Message is not for me.'),
736: ResultInfo('Friend request blocked.'),
737: ResultInfo('Not in my friend list.'),
738: ResultInfo('Friend listed by me.'),
739: ResultInfo('Not in my blackist.'),
740: ResultInfo('Incompatible account.'),
741: ResultInfo('Block setting change not allowed.'),
742: ResultInfo('Size limit exceeded.'),
743: ResultInfo('Operation not allowed.'),
744: ResultInfo('Not a network account.'),
745: ResultInfo('Notification not found.'),
746: ResultInfo('Preference not initialized.'),
747: ResultInfo('Friend request not allowed.'),
800: ResultInfo('Undefined error.'),
801: ResultInfo('Account library error.'),
802: ResultInfo('Token parse error.'),
803: ResultInfo('Reserved 3.'),
804: ResultInfo('Reserved 4.'),
805: ResultInfo('Reserved 5.'),
806: ResultInfo('Token expired.'),
807: ResultInfo('Validation failed.'),
808: ResultInfo('Invalid parameters.'),
809: ResultInfo('Principal ID unmatched.'),
810: ResultInfo('Reserved 10.'),
811: ResultInfo('Under maintenance.'),
812: ResultInfo('Unsupported version.'),
813: ResultInfo('Unknown error.')
}, {
(100, 199): 'Core',
(200, 299): 'DDL',
(300, 399): 'Rendezvous',
(400, 499): 'Python Core',
(500, 599): 'Transport',
(600, 699): 'DO Core',
(700, 799): 'FPD',
(800, 899): 'Authentication',
(1100, 1199): 'Ranking',
(1200, 1299): 'Data Store',
(1500, 1599): 'Service Item',
(1800, 1899): 'Matchmaking Referee',
(1900, 1999): 'Subscriber',
(2000, 2099): 'Ranking2',
})
act = Module('act (accounts)', {
0: ResultInfo('Success.'),
1: ResultInfo('Mail address not confirmed.'),
500: ResultInfo('Library error.'),
501: ResultInfo('Not initialized.'),
502: ResultInfo('Already initialized.'),
511: ResultInfo('Busy.'),
591: ResultInfo('Not implemented.'),
592: ResultInfo('Deprecated.'),
593: ResultInfo('Development only.'),
600: ResultInfo('Invalid argument.'),
601: ResultInfo('Invalid pointer.'),
602: ResultInfo('Out of range.'),
603: ResultInfo('Invalid size.'),
604: ResultInfo('Invalid format.'),
605: ResultInfo('Invalid handle.'),
606: ResultInfo('Invalid value.'),
700: ResultInfo('Internal error.'),
701: ResultInfo('End of stream.'),
710: ResultInfo('File error.'),
711: ResultInfo('File not found.'),
712: ResultInfo('File version mismatch.'),
713: ResultInfo('File I/O error.'),
714: ResultInfo('File type mismatch.'),
730: ResultInfo('Out of resources.'),
731: ResultInfo('Buffer is insufficient.'),
740: ResultInfo('Out of memory.'),
741: ResultInfo('Out of global heap.'),
742: ResultInfo('Out of cross-process heap.'),
744: ResultInfo('Out of MXML heap.'),
800: ResultInfo('Generic error.'),
801: ResultInfo('Open error.'),
802: ResultInfo('Read sys-config error.'),
810: ResultInfo('Generic error.'),
811: ResultInfo('Open error.'),
812: ResultInfo('Get info error.'),
820: ResultInfo('Generic error.'),
821: ResultInfo('Initialization failure.'),
822: ResultInfo('Get country code failure.'),
823: ResultInfo('Get language code failure.'),
850: ResultInfo('Generic error.'),
900: ResultInfo('Generic error.'),
901: ResultInfo('Open error.'),
1000: ResultInfo('Management error.'),
1001: ResultInfo('Not found.'),
1002: ResultInfo('Slots full.'),
1011: ResultInfo('Not loaded.'),
1012: ResultInfo('Already loaded.'),
1013: ResultInfo('Locked.'),
1021: ResultInfo('Not a network account.'),
1022: ResultInfo('Not a local account.'),
1023: ResultInfo('Not committed.'),
1101: ResultInfo('Network clock is invalid.'),
2000: ResultInfo('Authentication error.'),
# TODO: 2001-2644 (there aren't really that many errors)
2643: ResultInfo('Authentication is required.'),
2651: ResultInfo('Confirmation code is expired.'),
2661: ResultInfo('Mail address is not validated.'),
2662: ResultInfo('Excessive mail send requests.'),
2670: ResultInfo('Generic error.'),
2671: ResultInfo('General failure.'),
2672: ResultInfo('Declined.'),
2673: ResultInfo('Blacklisted.'),
2674: ResultInfo('Invalid credit card number.'),
2675: ResultInfo('Invalid credit card date.'),
2676: ResultInfo('Invalid credit card PIN.'),
2677: ResultInfo('Invalid postal code.'),
2678: ResultInfo('Invalid location.'),
2679: ResultInfo('Card is expired.'),
2680: ResultInfo('Credit card number is wrong.'),
2681: ResultInfo('PIN is wrong.'),
2800: ResultInfo('Banned.', is_ban=True),
2801: ResultInfo('Account is banned.', is_ban=True),
2802: ResultInfo('Account is banned from all services.', is_ban=True),
2803: ResultInfo('Account is banned from a particular game.', is_ban=True),
2804: ResultInfo('Account is banned from Nintendo\'s online service.', is_ban=True),
2805: ResultInfo('Account is banned from independent services.', is_ban=True),
2811: ResultInfo('Console is banned.', is_ban=True),
2812: ResultInfo('Console is banned from all services.', is_ban=True),
2813: ResultInfo('Console is banned from a particular game.', is_ban=True),
2814: ResultInfo('Console is banned from Nintendo\'s online service.', is_ban=True),
2815: ResultInfo('Console is banned from independent services.', is_ban=True),
2816: ResultInfo('Console is banned for an unknown duration, due to using modified/hacked files in online games like Splatoon.', is_ban=True),
2821: ResultInfo('Account is temporarily banned.', is_ban=True),
2822: ResultInfo('Account is temporarily banned from all services.', is_ban=True),
2823: ResultInfo('Account is temporarily banned from a particular game.', is_ban=True),
2824: ResultInfo('Account is temporarily banned from Nintendo\'s online service.', is_ban=True),
2825: ResultInfo('Acccount is temporarily banned from independent services.', is_ban=True),
2831: ResultInfo('Console is temporarily banned.', is_ban=True),
2832: ResultInfo('Console is temporarily banned from all services.', is_ban=True),
2833: ResultInfo('Console is temporarily banned from a particular game.', is_ban=True),
2834: ResultInfo('Console is temporarily banned from Nintendo\'s online service.', is_ban=True),
2835: ResultInfo('Console is temporarily banned from independent services.', is_ban=True),
2880: ResultInfo('Service is not provided.'),
2881: ResultInfo('Service is currently under maintenance.'),
2882: ResultInfo('Service is closed.'),
2883: ResultInfo('Nintendo Network is closed.'),
2884: ResultInfo('Service is not provided in this country.'),
2900: ResultInfo('Restriction error.'),
2901: ResultInfo('Restricted by age.'),
2910: ResultInfo('Restricted by parental controls.'),
2911: ResultInfo('In-game internet communication/chat is restricted.'),
2931: ResultInfo('Internal server error.'),
2932: ResultInfo('Unknown server error.'),
2998: ResultInfo('Unauthenticated after salvage.'),
2999: ResultInfo('Unknown authentication failure.'),
}, {
(0, 499): 'Internal',
(500, 599): 'Status changed',
(600, 699): 'Invalid argument',
(700, 709): 'Internal error',
(710, 729): 'File error',
(730, 799): 'Out of resources',
(800, 809): 'UC',
(810, 819): 'MCP',
(820, 849): 'ISO',
(850, 899): 'MXML',
(900, 999): 'IOS',
(1000, 1099): 'Account',
(2100, 2199): 'HTTP',
(2500, 2599): 'Account',
(2670, 2699): 'Credit Card',
(2800, 2835): 'Banned',
(2880, 2899): 'Not available', # not provided/under maintenance/no longer in service
})
nex = Module('nex (game servers)', {
102: ResultInfo('The reason for the error is unknown.'),
103: ResultInfo('The operation is currently not implemented.'),
104: ResultInfo('The operation specifies or accesses an invalid pointer.'),
105: ResultInfo('The operation was aborted.'),
106: ResultInfo('The operation raised an exception.'),
107: ResultInfo('An attempt was made to access data in an incorrect manner. This may be due to inadequate permission or the data, file, etc. not existing.'),
108: ResultInfo('The operation specifies or accesses an invalid DOHandle.'),
109: ResultInfo('The operation specifies or accesses an invalid index.'),
110: ResultInfo('The system could not allocate or access enough memory or disk space to perform the specified operation.'),
111: ResultInfo('Invalid argument were passed with the operation. The argument(s) may be out of range or invalid.'),
112: ResultInfo('The operation did not complete within the specified timeout for that operation.'),
113: ResultInfo('Initialization of the component failed.'),
114: ResultInfo('The call failed to initialize.'),
115: ResultInfo('An error occurred during registration.'),
116: ResultInfo('The buffer is too large to be sent.'),
117: ResultInfo('Invalid lock state.'),
118: ResultInfo('Invalid sequence.'),
301: ResultInfo('Connection was unable to be established, either with the Rendez-Vous back end or a Peer.'),
302: ResultInfo('The Principal could not be authenticated by the Authentication Service.'),
303: ResultInfo('The Principal tried to log in with an invalid user name, i.e. the user name does not exist in the database.'),
304: ResultInfo('The Principal either tried to log in with an invalid password for the provided user name or tried to join a Gathering with an invalid password.'),
305: ResultInfo('The provided user name already exists in the database. All usernames must be unique.'),
306: ResultInfo('The Principal\'s account still exists in the database but the account has been disabled.', is_ban=True),
307: ResultInfo('The Principal\'s account still exists in the database but the account has expired.'),
308: ResultInfo('The Principal does not have the Capabilities to perform concurrent log ins, i.e. at any given time only one log-in may be maintained.'),
309: ResultInfo('Data encryption failed.'),
310: ResultInfo('The operation specifies or accesses an invalid PrincipalID.'),
311: ResultInfo('Maximum connnection number is reached.'),
312: ResultInfo('Invalid GID.'),
313: ResultInfo('Invalid Control script ID.'),
314: ResultInfo('Invalid operation in live/production environment.'),
315: ResultInfo('Duplicate entry.'),
346: ResultInfo('NNID is permanently banned.', is_ban=True),
501: ResultInfo('The reason for the error is unknown.'),
502: ResultInfo('Network connection was unable to be established.'),
503: ResultInfo('The URL contained in the StationURL is invalid. The syntax may be incorrect.'),
504: ResultInfo('The key used to authenticate a given station is invalid. The secure transport layer uses secret-key based cryptography to ensure the integrity and confidentiality of data sent across the network.'),
505: ResultInfo('The specified transport type is invalid.'),
506: ResultInfo('The Station is already connected via another EndPoint.'),
507: ResultInfo('The data could not be sent across the network. This could be due to an invalid message, packet, or buffer.'),
508: ResultInfo('The operation did not complete within the specified timeout for that operation.'),
509: ResultInfo('The network connection was reset.'),
510: ResultInfo('The destination Station did not authenticate itself properly.'),
511: ResultInfo('3rd-party server or device answered with an error code according to protocol used e.g. HTTP error code.'),
}, {
(100, 199): 'Core',
(200, 299): 'DDL',
(300, 399): 'Rendezvous',
(400, 499): 'Python Core',
(500, 599): 'Transport',
(600, 699): 'DO Core',
(700, 799): 'FPD',
(800, 899): 'Authentication',
(1100, 1199): 'Ranking',
(1200, 1299): 'Data Store',
(1500, 1599): 'Service Item',
(1800, 1899): 'Matchmaking Referee',
(1900, 1999): 'Subscriber',
(2000, 2099): 'Ranking2',
})
eshop_api = Module('eshop(api)', {
3190: ResultInfo('Wishlist is full.')
})
eshop_web = Module('eshop (web)', {
9000: ResultInfo('Close application (Connection timeout issue?).'),
9001: ResultInfo('Retriable.'),
9002: ResultInfo('Online services are undergoing maintenance.'),
9003: ResultInfo('The online services are discontinued and thus are no longer available.'),
9100: ResultInfo('Invalid template.')
})
unknown2 = Module('unknown (browser?)', {
1037: ResultInfo('Incorrect permissions for the default index.html file which prevents the Internet Browser from reading it.', '[To fix it, follow these steps.](https://wiiu.hacks.guide/#/fix-errcode-112-1037)'),
})
olv = Module('olv (miiverse)', {
1009: ResultInfo('Console is permanently banned from Miiverse.', is_ban=True),
5004: ResultInfo('The Miiverse service has been discontinued.')
})
eshop_unk = Module('eShop (unknown)', {
9622: ResultInfo('Error when attempting to add funds. Check that the payment method is correct or try again later.')
})
fs = Module('fs', {
1031: ResultInfo('The disc could not be read or is unsupported (i.e. not a Wii or Wii U game). Try cleaning the disc or lens if it is a supported title.'),
2031: ResultInfo('The disc could not be read or is unsupported (i.e. not a Wii or Wii U game). Try cleaning the disc or lens if it is a supported title.'),
3032: ResultInfo('Error when attempting to read caused by a permission error.')
})
syserr = Module('system error', {
101: ResultInfo('Generic error. Can happen when formatting a console that has CBHC installed.'),
102: ResultInfo('Error in SLC/MLC or USB.'),
103: ResultInfo('The MLC system memory is corrupted.'),
104: ResultInfo('The SLC system memory is corrupted.'),
105: ResultInfo('The USB storage is corrupted.'),
})
unknown = Module('unknown/misc.', {
9999: ResultInfo('Usually indicates an invalid signature, ticket, or corrupted data. Typically happens when running an unsigned program without CFW/signature patches.')
})
# We have some modules partially documented, those that aren't have dummy Modules.
modules = {
101: fp,
102: act,
103: Module('ac (internet connection)'),
104: Module('boss(spotpass)'),
105: Module('nim (title installation'),
106: nex,
107: eshop_api,
111: eshop_web,
112: unknown2,
115: olv,
118: Module('pia (peer-to-peer)'),
124: Module('ec (e-commerce)'),
126: eshop_unk,
150: fs,
151: Module('kpad (wiimote)'),
155: Module('save'),
160: syserr,
165: Module('vpad (gamepad)'),
166: Module('aoc (dlc)'),
187: Module('nfp (amiibo)'),
199: unknown
}
# regex for Wii U result code format "1XX-YYYY"
RE = re.compile(r'1\d{2}-\d{4}')
CONSOLE_NAME = 'Nintendo Wii U'
# Suggested color to use if displaying information through a Discord bot's embed
COLOR = 0x009AC7
def is_valid(error):
return RE.match(error)
def construct_support(ret, mod, desc):
category = modules.get(mod, Module(''))
if category.name:
ret.add_field(ConsoleErrorField('Category', message_str=category.name))
else:
ret.add_field(ConsoleErrorField('Category', supplementary_value=mod))
summary = category.get_summary(desc)
if summary:
ret.add_field(ConsoleErrorField('Summary', message_str=summary))
description = category.get_error(desc)
if description is not None and description.description:
ret.add_field(ConsoleErrorField('Description', message_str=description.description))
if description.support_url:
ret.add_field(ConsoleErrorField('Further information', message_str=description.support_url))
if description.is_ban:
ret.add_field(BANNED_FIELD)
ret.color = WARNING_COLOR
else:
ret.add_field(UNKNOWN_CATEGORY_DESCRIPTION)
return ret
def get(error):
mod = int(error[:3])
desc = int(error[4:])
ret = ConsoleErrorInfo(error, CONSOLE_NAME, COLOR)
return construct_support(ret, mod, desc)