Jump to content
  • entries
    4
  • comments
    15
  • views
    731

Dumping All Ini Settings Read By The Game


traison

234 views

1. Foreword

 

Since there seems to be quite a bit of confusion on the internet about which ini keys the game reads, here's a method for dumping all sections, keys and default values straight from the exe. It uses an x64dbg script to dump everything from the winapi calls. Data is filtered in a separate Python script. The setup is quite barebones, but it does the job.

 

2. Disclaimer

 

The results have not been verified, but at the time of writing I have no reason to believe this is incorrect.

 

3. Notes

  • A key read is not a key used. This is not a list of ini settings that change something in the game. If the key you're looking for is not dumped by this tool then it absolutely won't work: a key that is never read, can never be used.
  • Default values are the values passed to the winapi function, not what is in the default ini files. stepmodifications.org most likely has a different definition of defaults.
  • GetPrivateProfileIntA internally uses GetPrivateProfileStringA. The game does not use the wide char variants.

4. Permissions

 

You may use this without credit, but if you do create something useful with this, please post a link to it.

 

5. Summoning A Flame Atronach In The Real World

 

x64dbg script:

bp GetPrivateProfileStringA
SetBreakpointCommand GetPrivateProfileStringa, "scriptcmd call debug_getstring_a"
bp GetPrivateProfileStringW
SetBreakpointCommand GetPrivateProfileStringW, "scriptcmd call debug_getstring_w"

goto main

debug_getstring_a:
rtr
mem_file = ReadQword(rsp+30)
mem_section = ReadQword(rsp-38)
mem_key = ReadQword(rsp-30)
mem_value = ReadQword(rsp-48)
mem_default = ReadQword(rsp-28)
log "GetPrivateProfileStringA({s:mem_section}, {s:mem_key}, {s:mem_default}, {s:mem_value}, ?, {s:mem_file})"
goto main

debug_getstring_w:
rtr
mem_file = ReadQword(rsp+30)
mem_section = ReadQword(rsp-38)
mem_key = ReadQword(rsp-30)
mem_value = ReadQword(rsp-48)
mem_default = ReadQword(rsp-28)
log "GetPrivateProfileStringW({s:mem_section}, {s:mem_key}, {s:mem_default}, {s:mem_value}, ?, {s:mem_file})"
goto main

main:
run

 

Python x64dbg dump/log filter and data reformatter:

Spoiler
import sys, os

def main(argc, argv):
    raw_data_file = 'data_raw.txt'
    files = {}
    
    with open(raw_data_file, 'r') as f:
        for line in f:
            line = line.strip()
            
            if line.startswith('GetPrivateProfileString'):
                is_wide = line[23] == 'W'
                raw_args = line[25:-1]
                args = parse_args(raw_args)
                #args = [s.strip(' "') for s in args]
                
                app_name, key_name, default, returned_string, unk1, file = args
                
                if file == '???' or file == '':
                    continue
                
                section = app_name # Translate Microslop to makes-sense.
                section = section.strip() # NavMesh section is named " NavMesh"
                
                file = file.replace('\\\\', '\\')
                file = file.replace('/', '\\') # Normalize directory separator
                file_id = get_file_id(file)
                file_id = file_id.strip('\\') # Strip leading/trailing slash
                file_id = file_id.replace('\\', '__') # Remove slashes
                file_id = file_id.replace(' ', '_') # Remove spaces
                
                if not file_id in files:
                    files[file_id] = {}
                    #print(f'{file} -> {file_id}')
                
                if not section in files[file_id]:
                    files[file_id][section] = []
                
                files[file_id][section].append({
                    'key_name': key_name,
                    'default': default,
                    'is_wide': is_wide
                })
    
    
    dump_files(files)


def dump_files(files):
    if not os.path.isdir('output_inis'):
        os.makedirs('output_inis')
    
    for file_id in files:
        #print(f'{file} {len(file)}')
        
        with open('output_inis\\' + file_id, 'w') as f:
            for section_id in files[file_id]:
                section = files[file_id][section_id]
                
                f.write(f'\n[{section_id}]\n')
                
                for kv in section:
                    key_name = kv['key_name']
                    default = kv['default']
                    
                    f.write(f'{key_name}={default}\n')

    
def parse_args(raw_args):
    args = []
    arg = ''
    in_quotes = False
    
    for i in range(len(raw_args)):
        c = raw_args[i]
        
        if c == '"':
            if in_quotes:
                in_quotes = False
            else:
                in_quotes = True
        elif c == ',':
            if in_quotes:
                arg += c
            else:
                args.append(arg)
                arg = ''
        elif in_quotes or c == '?':
            arg += c
    
    #if len(arg) > 0:
    args.append(arg)
    
    return args


def get_file_id(file):
    count = 0
    
    for i in range(len(file) - 1, 0, -1):
        c = file[i]
        
        if c == '\\' or c == '/':
            count += 1
            
            if count == 2:
                return file[i:]
    
    return file

                    
if __name__ == '__main__':
    main(len(sys.argv), sys.argv)

 

 

All ini keys dumped from SkyrimSE.exe 1.6.1170.0 (7a44a52dfc92d78f934c4d12ed92f494) are in the attachment below.

 

 

all_skyrimse_1_6_1170_0.txt

Edited by traison

1 Comment


Recommended Comments

traison

Posted (edited)

  • Turns out the original all_skyrimse_1_6_1170_0.txt file was missing 42 ini keys. A new list containing everything has been uploaded.
  • The data filtering script I was using seems to have skipped over some values. A new filtering script has been uploaded.
  • The x64dbg script was updated to include GetPrivateProfileStringW so that it can also log certain mods accessing ini files.
Edited by traison
×
×
  • Create New...