mirror of
				https://github.com/Atmosphere-NX/Atmosphere.git
				synced 2025-10-31 03:05:48 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			435 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			435 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #
 | |
| # Copyright (c) 2018-2020 Atmosphère-NX
 | |
| #
 | |
| # This program is free software; you can redistribute it and/or modify it
 | |
| # under the terms and conditions of the GNU General Public License,
 | |
| # version 2, as published by the Free Software Foundation.
 | |
| #
 | |
| # This program is distributed in the hope it will be useful, but WITHOUT
 | |
| # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | |
| # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 | |
| # more details.
 | |
| #
 | |
| # You should have received a copy of the GNU General Public License
 | |
| # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | |
| 
 | |
| # erpt.py: Autogeneration tool for <stratosphere/erpt/erpt_ids.autogen.hpp>
 | |
| 
 | |
| import nxo64
 | |
| import sys, os, string
 | |
| from struct import unpack as up, pack as pk
 | |
| 
 | |
| LOAD_BASE = 0x7100000000
 | |
| 
 | |
| HEADER = '''/*
 | |
|  * Copyright (c) 2018-2020 Atmosphère-NX
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify it
 | |
|  * under the terms and conditions of the GNU General Public License,
 | |
|  * version 2, as published by the Free Software Foundation.
 | |
|  *
 | |
|  * This program is distributed in the hope it will be useful, but WITHOUT
 | |
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 | |
|  * more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License
 | |
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | |
|  */
 | |
| #pragma once
 | |
| #include <vapours.hpp>
 | |
| 
 | |
| /* NOTE: This file is auto-generated. */
 | |
| /* Do not make edits to this file by hand. */
 | |
| 
 | |
| '''
 | |
| 
 | |
| if sys.version_info[0] == 3:
 | |
|     iter_range = range
 | |
|     int_types = (int,)
 | |
|     ascii_string = lambda b: b.decode('ascii')
 | |
|     bytes_to_list = lambda b: list(b)
 | |
|     list_to_bytes = lambda l: bytes(l)
 | |
| else:
 | |
|     iter_range = xrange
 | |
|     int_types = (int, long)
 | |
|     ascii_string = lambda b: str(b)
 | |
|     bytes_to_list = lambda b: map(ord, b)
 | |
|     list_to_bytes = lambda l: ''.join(map(chr, l))
 | |
| 
 | |
| (DT_NULL, DT_NEEDED, DT_PLTRELSZ, DT_PLTGOT, DT_HASH, DT_STRTAB, DT_SYMTAB, DT_RELA, DT_RELASZ,
 | |
|  DT_RELAENT, DT_STRSZ, DT_SYMENT, DT_INIT, DT_FINI, DT_SONAME, DT_RPATH, DT_SYMBOLIC, DT_REL,
 | |
|  DT_RELSZ, DT_RELENT, DT_PLTREL, DT_DEBUG, DT_TEXTREL, DT_JMPREL, DT_BIND_NOW, DT_INIT_ARRAY,
 | |
|  DT_FINI_ARRAY, DT_INIT_ARRAYSZ, DT_FINI_ARRAYSZ, DT_RUNPATH, DT_FLAGS) = iter_range(31)
 | |
| DT_GNU_HASH = 0x6ffffef5
 | |
| DT_VERSYM = 0x6ffffff0
 | |
| DT_RELACOUNT = 0x6ffffff9
 | |
| DT_RELCOUNT = 0x6ffffffa
 | |
| DT_FLAGS_1 = 0x6ffffffb
 | |
| DT_VERDEF = 0x6ffffffc
 | |
| DT_VERDEFNUM = 0x6ffffffd
 | |
| 
 | |
| STT_NOTYPE = 0
 | |
| STT_OBJECT = 1
 | |
| STT_FUNC = 2
 | |
| STT_SECTION = 3
 | |
| 
 | |
| STB_LOCAL = 0
 | |
| STB_GLOBAL = 1
 | |
| STB_WEAK = 2
 | |
| 
 | |
| R_ARM_ABS32 = 2
 | |
| R_ARM_TLS_DESC = 13
 | |
| R_ARM_GLOB_DAT = 21
 | |
| R_ARM_JUMP_SLOT = 22
 | |
| R_ARM_RELATIVE = 23
 | |
| 
 | |
| R_AARCH64_ABS64 = 257
 | |
| R_AARCH64_GLOB_DAT = 1025
 | |
| R_AARCH64_JUMP_SLOT = 1026
 | |
| R_AARCH64_RELATIVE = 1027
 | |
| R_AARCH64_TLSDESC = 1031
 | |
| 
 | |
| CATEGORIES = {
 | |
|     0   : 'Test',
 | |
|     1   : 'ErrorInfo',
 | |
|     2   : 'ConnectionStatusInfo',
 | |
|     3   : 'NetworkInfo',
 | |
|     4   : 'NXMacAddressInfo',
 | |
|     5   : 'StealthNetworkInfo',
 | |
|     6   : 'LimitHighCapacityInfo',
 | |
|     7   : 'NATTypeInfo',
 | |
|     8   : 'WirelessAPMacAddressInfo',
 | |
|     9   : 'GlobalIPAddressInfo',
 | |
|     10  : 'EnableWirelessInterfaceInfo',
 | |
|     11  : 'EnableWifiInfo',
 | |
|     12  : 'EnableBluetoothInfo',
 | |
|     13  : 'EnableNFCInfo',
 | |
|     14  : 'NintendoZoneSSIDListVersionInfo',
 | |
|     15  : 'LANAdapterMacAddressInfo',
 | |
|     16  : 'ApplicationInfo',
 | |
|     17  : 'OccurrenceInfo',
 | |
|     18  : 'ProductModelInfo',
 | |
|     19  : 'CurrentLanguageInfo',
 | |
|     20  : 'UseNetworkTimeProtocolInfo',
 | |
|     21  : 'TimeZoneInfo',
 | |
|     22  : 'ControllerFirmwareInfo',
 | |
|     23  : 'VideoOutputInfo',
 | |
|     24  : 'NANDFreeSpaceInfo',
 | |
|     25  : 'SDCardFreeSpaceInfo',
 | |
|     26  : 'ScreenBrightnessInfo',
 | |
|     27  : 'AudioFormatInfo',
 | |
|     28  : 'MuteOnHeadsetUnpluggedInfo',
 | |
|     29  : 'NumUserRegisteredInfo',
 | |
|     30  : 'DataDeletionInfo',
 | |
|     31  : 'ControllerVibrationInfo',
 | |
|     32  : 'LockScreenInfo',
 | |
|     33  : 'InternalBatteryLotNumberInfo',
 | |
|     34  : 'LeftControllerSerialNumberInfo',
 | |
|     35  : 'RightControllerSerialNumberInfo',
 | |
|     36  : 'NotificationInfo',
 | |
|     37  : 'TVInfo',
 | |
|     38  : 'SleepInfo',
 | |
|     39  : 'ConnectionInfo',
 | |
|     40  : 'NetworkErrorInfo',
 | |
|     41  : 'FileAccessPathInfo',
 | |
|     42  : 'GameCardCIDInfo',
 | |
|     43  : 'NANDCIDInfo',
 | |
|     44  : 'MicroSDCIDInfo',
 | |
|     45  : 'NANDSpeedModeInfo',
 | |
|     46  : 'MicroSDSpeedModeInfo',
 | |
|     47  : 'GameCardSpeedModeInfo',
 | |
|     48  : 'UserAccountInternalIDInfo',
 | |
|     49  : 'NetworkServiceAccountInternalIDInfo',
 | |
|     50  : 'NintendoAccountInternalIDInfo',
 | |
|     51  : 'USB3AvailableInfo',
 | |
|     52  : 'CallStackInfo',
 | |
|     53  : 'SystemStartupLogInfo',
 | |
|     54  : 'RegionSettingInfo',
 | |
|     55  : 'NintendoZoneConnectedInfo',
 | |
|     56  : 'ForceSleepInfo',
 | |
|     57  : 'ChargerInfo',
 | |
|     58  : 'RadioStrengthInfo',
 | |
|     59  : 'ErrorInfoAuto',
 | |
|     60  : 'AccessPointInfo',
 | |
|     61  : 'ErrorInfoDefaults',
 | |
|     62  : 'SystemPowerStateInfo',
 | |
|     63  : 'PerformanceInfo',
 | |
|     64  : 'ThrottlingInfo',
 | |
|     65  : 'GameCardErrorInfo',
 | |
|     66  : 'EdidInfo',
 | |
|     67  : 'ThermalInfo',
 | |
|     68  : 'CradleFirmwareInfo',
 | |
|     69  : 'RunningApplicationInfo',
 | |
|     70  : 'RunningAppletInfo',
 | |
|     71  : 'FocusedAppletHistoryInfo',
 | |
|     72  : 'CompositorInfo',
 | |
|     73  : 'BatteryChargeInfo',
 | |
|     74  : 'NANDExtendedCsd',
 | |
|     75  : 'NANDPatrolInfo',
 | |
|     76  : 'NANDErrorInfo',
 | |
|     77  : 'NANDDriverLog',
 | |
|     78  : 'SdCardSizeSpec',
 | |
|     79  : 'SdCardErrorInfo',
 | |
|     80  : 'SdCardDriverLog ',
 | |
|     81  : 'FsProxyErrorInfo',
 | |
|     82  : 'SystemAppletSceneInfo',
 | |
|     83  : 'VideoInfo',
 | |
|     84  : 'GpuErrorInfo',
 | |
|     85  : 'PowerClockInfo',
 | |
|     86  : 'AdspErrorInfo',
 | |
|     87  : 'NvDispDeviceInfo',
 | |
|     88  : 'NvDispDcWindowInfo',
 | |
|     89  : 'NvDispDpModeInfo',
 | |
|     90  : 'NvDispDpLinkSpec',
 | |
|     91  : 'NvDispDpLinkStatus',
 | |
|     92  : 'NvDispDpHdcpInfo',
 | |
|     93  : 'NvDispDpAuxCecInfo',
 | |
|     94  : 'NvDispDcInfo',
 | |
|     95  : 'NvDispDsiInfo',
 | |
|     96  : 'NvDispErrIDInfo',
 | |
|     97  : 'SdCardMountInfo',
 | |
|     98  : 'RetailInteractiveDisplayInfo',
 | |
|     99  : 'CompositorStateInfo',
 | |
|     100 : 'CompositorLayerInfo',
 | |
|     101 : 'CompositorDisplayInfo',
 | |
|     102 : 'CompositorHWCInfo',
 | |
|     103 : 'MonitorCapability',
 | |
|     104 : 'ErrorReportSharePermissionInfo',
 | |
|     105 : 'MultimediaInfo',
 | |
|     106 : 'ConnectedControllerInfo',
 | |
|     107 : 'FsMemoryInfo',
 | |
|     108 : 'UserClockContextInfo',
 | |
|     109 : 'NetworkClockContextInfo',
 | |
|     110 : 'AcpGeneralSettingsInfo',
 | |
|     111 : 'AcpPlayLogSettingsInfo',
 | |
|     112 : 'AcpAocSettingsInfo',
 | |
|     113 : 'AcpBcatSettingsInfo',
 | |
|     114 : 'AcpStorageSettingsInfo',
 | |
|     115 : 'AcpRatingSettingsInfo',
 | |
|     116 : 'MonitorSettings',
 | |
|     117 : 'RebootlessSystemUpdateVersionInfo',
 | |
|     118 : 'NifmConnectionTestInfo',
 | |
|     119 : 'PcieLoggedStateInfo',
 | |
|     120 : 'NetworkSecurityCertificateInfo',
 | |
|     121 : 'AcpNeighborDetectionInfo',
 | |
|     122 : 'GpuCrashInfo',
 | |
|     123 : 'UsbStateInfo',
 | |
|     124 : 'NvHostErrInfo',
 | |
|     125 : 'RunningUlaInfo',
 | |
|     126 : 'InternalPanelInfo',
 | |
|     127 : 'ResourceLimitLimitInfo',
 | |
|     128 : 'ResourceLimitPeakInfo',
 | |
|     129 : 'TouchScreenInfo',
 | |
|     130 : 'AcpUserAccountSettingsInfo',
 | |
|     131 : 'AudioDeviceInfo',
 | |
|     132 : 'AbnormalWakeInfo',
 | |
| }
 | |
| 
 | |
| FIELD_TYPES = {
 | |
|     0  : 'FieldType_NumericU64',
 | |
|     1  : 'FieldType_NumericU32',
 | |
|     2  : 'FieldType_NumericI64',
 | |
|     3  : 'FieldType_NumericI32',
 | |
|     4  : 'FieldType_String',
 | |
|     5  : 'FieldType_U8Array',
 | |
|     6  : 'FieldType_U32Array',
 | |
|     7  : 'FieldType_U64Array',
 | |
|     8  : 'FieldType_I32Array',
 | |
|     9  : 'FieldType_I64Array',
 | |
|     10 : 'FieldType_Bool',
 | |
|     11 : 'FieldType_NumericU16',
 | |
|     12 : 'FieldType_NumericU8',
 | |
|     13 : 'FieldType_NumericI16',
 | |
|     14 : 'FieldType_NumericI8',
 | |
|     15 : 'FieldType_I8Array',
 | |
| }
 | |
| 
 | |
| FIELD_FLAGS = {
 | |
|     0 : 'FieldFlag_None',
 | |
|     1 : 'FieldFlag_Encrypt',
 | |
| }
 | |
| 
 | |
| def get_full(nxo):
 | |
|     full = nxo.text[0]
 | |
|     if nxo.ro[2] >= len(full):
 | |
|         full += b'\x00' * (nxo.ro[2] - len(full))
 | |
|     else:
 | |
|         full = full[:nxo.ro[2]]
 | |
|     full += nxo.ro[0]
 | |
|     if nxo.data[2] > len(full):
 | |
|         full += b'\x00' * (nxo.data[2] - len(full))
 | |
|     full += nxo.data[0]
 | |
| 
 | |
|     undef_count = 0
 | |
|     for s in nxo.symbols:
 | |
|         if not s.shndx and s.name:
 | |
|             undef_count += 1
 | |
|     last_ea = max(LOAD_BASE + end for start, end, name, kind in nxo.sections)
 | |
|     undef_entry_size = 8
 | |
|     undef_ea = ((last_ea + 0xFFF) & ~0xFFF) + undef_entry_size # plus 8 so we don't end up on the "end" symbol
 | |
| 
 | |
|     for i,s in enumerate(nxo.symbols):
 | |
|         if not s.shndx and s.name:
 | |
|             #idaapi.create_data(undef_ea, idc.FF_QWORD, 8, idaapi.BADADDR)
 | |
|             #idaapi.force_name(undef_ea, s.name)
 | |
|             s.resolved = undef_ea
 | |
|             undef_ea += undef_entry_size
 | |
|         elif i != 0:
 | |
|             assert s.shndx
 | |
|             s.resolved = LOAD_BASE + s.value
 | |
|             if s.name:
 | |
|                 if s.type == STT_FUNC:
 | |
|                     #print(hex(s.resolved), s.name)
 | |
|                     #idaapi.add_entry(s.resolved, s.resolved, s.name, 0)
 | |
|                     pass
 | |
|                 else:
 | |
|                     #idaapi.force_name(s.resolved, s.name)
 | |
|                     pass
 | |
|         else:
 | |
|             # NULL symbol
 | |
|             s.resolved = 0
 | |
| 
 | |
|     def put_dword(z, target, val):
 | |
|         return z[:target] + pk('<I', val) + z[target+4:]
 | |
|     def put_qword(z, target, val):
 | |
|         return z[:target] + pk('<Q', val) + z[target+8:]
 | |
|     def get_dword(z, target):
 | |
|         return up('<I', z[target:target+4])[0]
 | |
| 
 | |
|     for offset, r_type, sym, addend in nxo.relocations:
 | |
|         #print offset, r_type, sym, addend
 | |
|         target = offset + LOAD_BASE
 | |
|         if r_type in (R_ARM_GLOB_DAT, R_ARM_JUMP_SLOT, R_ARM_ABS32):
 | |
|             if not sym:
 | |
|                 print('error: relocation at %X failed' % target)
 | |
|             else:
 | |
|                 full = put_dword(full, target, sym.resolved)
 | |
|         elif r_type == R_ARM_RELATIVE:
 | |
|             full = put_dword(full, target, get_dword(full, target) + LOAD_BASE)
 | |
|         elif r_type in (R_AARCH64_GLOB_DAT, R_AARCH64_JUMP_SLOT, R_AARCH64_ABS64):
 | |
|             full = put_qword(full, target, sym.resolved + addend)
 | |
|         elif r_type == R_AARCH64_RELATIVE:
 | |
|             full = put_qword(full, target, LOAD_BASE + addend)
 | |
|         else:
 | |
|             print('TODO r_type %d' % (r_type,))
 | |
|     return full
 | |
| 
 | |
| def locate_fields(full):
 | |
|     start = ['TestU64', 'TestU32', 'TestI64', 'TestI32']
 | |
|     inds = [LOAD_BASE + full.index('%s\x00' % s) for s in start]
 | |
|     target = pk('<QQQQ', inds[0], inds[1], inds[2], inds[3])
 | |
|     return full.index(target)
 | |
| 
 | |
| def read_string(full, ofs):
 | |
|     s = ''
 | |
|     if ofs >= len(full):
 | |
|         return ''
 | |
|     while full[ofs] != '\x00':
 | |
|         s += full[ofs]
 | |
|         ofs += 1
 | |
|         if ofs >= len(full):
 | |
|             return ''
 | |
|     return s
 | |
| 
 | |
| def is_valid_field_name(s):
 | |
|     ALLOWED = string.lowercase + string.uppercase + string.digits + '_'
 | |
|     if not s:
 | |
|         return False
 | |
|     for c in s:
 | |
|         if not c in ALLOWED:
 | |
|             return False
 | |
|     return True
 | |
| 
 | |
| def parse_fields(full, table):
 | |
|     fields = []
 | |
|     ofs = 0
 | |
|     while True:
 | |
|         val = up('<Q', full[table + ofs:table + ofs + 8])[0]
 | |
|         if (val & 0xFFFFFFFF00000000) != LOAD_BASE:
 | |
|             break
 | |
|         s = read_string(full, val - LOAD_BASE)
 | |
|         if not is_valid_field_name(s):
 | |
|             break
 | |
|         fields.append(s)
 | |
|         ofs += 8
 | |
|     return fields
 | |
| 
 | |
| def find_categories(full, num_fields):
 | |
|     KNOWN = [0] * 10 + [1] * 2 + [0x3B] * 2
 | |
|     ind = full.index(''.join(pk('<I', i) for i in KNOWN))
 | |
|     return list(up('<'+'I'*num_fields, full[ind:ind+4*num_fields]))
 | |
| 
 | |
| def find_types(full, num_fields):
 | |
|     KNOWN     = range(10) + [4, 4, 2, 4]
 | |
|     KNOWN_OLD = range(10) + [4, 4, 0, 4]
 | |
|     try:
 | |
|         ind = full.index(''.join(pk('<I', i) for i in KNOWN))
 | |
|     except ValueError:
 | |
|         ind = full.index(''.join(pk('<I', i) for i in KNOWN_OLD))
 | |
|     return list(up('<'+'I'*num_fields, full[ind:ind+4*num_fields]))
 | |
| 
 | |
| def find_flags(full, num_fields):
 | |
|     KNOWN = '\x00' + ('\x01'*6) + '\x00\x01\x01\x00'
 | |
|     if num_fields < 443 + len(KNOWN):
 | |
|         return [0] * num_fields
 | |
|     ind = full.index(KNOWN) - 443
 | |
|     return list(up('<'+'B'*num_fields, full[ind:ind+num_fields]))
 | |
| 
 | |
| def cat_to_string(c):
 | |
|     return CATEGORIES[c] if c in CATEGORIES else 'Category_Unknown%d' % c
 | |
| 
 | |
| def typ_to_string(t):
 | |
|     return FIELD_TYPES[t] if t in FIELD_TYPES else 'FieldType_Unknown%d' % t
 | |
| 
 | |
| def flg_to_string(f):
 | |
|     return FIELD_FLAGS[f] if f in FIELD_FLAGS else 'FieldFlag_Unknown%d' % f
 | |
| 
 | |
| def main(argc, argv):
 | |
|     if argc != 2 and not (argc == 3 and argv[1] == '-f'):
 | |
|         print 'Usage: %s [-f] erpt_nso' % argv[0]
 | |
|         return 1
 | |
|     f = open(argv[-1], 'rb')
 | |
|     nxo = nxo64.load_nxo(f)
 | |
|     full = get_full(nxo)
 | |
|     field_table = locate_fields(full)
 | |
|     fields = parse_fields(full, field_table)
 | |
|     NUM_FIELDS = len(fields)
 | |
|     cats = find_categories(full, NUM_FIELDS)
 | |
|     types = find_types(full, NUM_FIELDS)
 | |
|     flags = find_flags(full, NUM_FIELDS)
 | |
|     print 'Identified %d fields.' % NUM_FIELDS
 | |
|     mf = max(len(s) for s in fields)
 | |
|     mc = max(len(cat_to_string(c)) for c in cats)
 | |
|     mt = max(len(typ_to_string(t)) for t in types)
 | |
|     ml = max(len(flg_to_string(f)) for f in flags)
 | |
|     if argc == 3:
 | |
|         mf, mc, mt, ml = (64, 48, 32, 32)
 | |
|     format_string = '- %%-%ds %%-%ds %%-%ds %%-%ds' % (mf+1, mc+1, mt+1, ml)
 | |
|     for i in xrange(NUM_FIELDS):
 | |
|         f, c, t, l = fields[i], cat_to_string(cats[i]), typ_to_string(types[i]), flg_to_string(flags[i])
 | |
|         print format_string % (f+',', c+',', t+',', l)
 | |
|     with open(argv[-1]+'.hpp', 'w') as out:
 | |
|         out.write(HEADER)
 | |
|         out.write('#define AMS_ERPT_FOREACH_FIELD_TYPE(HANDLER) \\\n')
 | |
|         for tp in sorted(list(set(types + FIELD_TYPES.keys()))):
 | |
|             out.write(('    HANDLER(%%-%ds %%-2d) \\\n' % (mt+1)) % (typ_to_string(tp)+',', tp))
 | |
|         out.write('\n')
 | |
|         out.write('#define AMS_ERPT_FOREACH_CATEGORY(HANDLER) \\\n')
 | |
|         for ct in sorted(list(set(cats + CATEGORIES.keys()))):
 | |
|             out.write(('    HANDLER(%%-%ds %%-3d) \\\n' % (mc+1)) % (cat_to_string(ct)+',', ct))
 | |
|         out.write('\n')
 | |
|         out.write('#define AMS_ERPT_FOREACH_FIELD(HANDLER) \\\n')
 | |
|         for i in xrange(NUM_FIELDS):
 | |
|             f, c, t, l = fields[i], cats[i], types[i], flags[i]
 | |
|             out.write(('    HANDLER(%%-%ds %%-4s %%-%ds %%-%ds %%-%ds) \\\n' % (mf+1, mc+1, mt+1, ml)) % (f+',', '%d,'%i, cat_to_string(c)+',', typ_to_string(t)+',', flg_to_string(l)))
 | |
|         out.write('\n')
 | |
|     return 0
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|     try:
 | |
|         ret = main(len(sys.argv), sys.argv)
 | |
|     except:
 | |
|         ret = 1
 | |
|         print 'exception'
 | |
|     sys.exit(ret) |