* write dlc to [app:dlcs] inside .ini

* fix a mistake when mixing output from generate_emu_config tool
* ensure output from generate_emu_config tool is written as soon as possible in case another stage threw an exception
* fix a bug when writing .ini, ensure everything is a string
* new readme for the migration tool
This commit is contained in:
otavepto 2024-04-20 17:30:04 +02:00
parent 0c4344e1bf
commit 0e694f1e86
6 changed files with 160 additions and 121 deletions

View File

@ -616,47 +616,38 @@ static std::set<std::string> parse_supported_languages(class Local_Storage *loca
return supported_languages;
}
// DLC.txt
// app::dlcs
static void parse_dlc(class Settings *settings_client, class Settings *settings_server)
{
std::string dlc_config_path = Local_Storage::get_game_settings_path() + "DLC.txt";
std::ifstream input( utf8_decode(dlc_config_path) );
if (input.is_open()) {
common_helpers::consume_bom(input);
settings_client->unlockAllDLC(false);
settings_server->unlockAllDLC(false);
PRINT_DEBUG("Locking all DLC");
constexpr static const char unlock_all_key[] = "unlock_all";
for( std::string line; std::getline( input, line ); ) {
if (!line.empty() && line.front() == '#') {
continue;
}
if (!line.empty() && line.back() == '\n') {
line.pop_back();
}
if (!line.empty() && line.back() == '\r') {
line.pop_back();
}
std::size_t deliminator = line.find("=");
if (deliminator != 0 && deliminator != std::string::npos && deliminator != line.size()) {
AppId_t appid = stol(line.substr(0, deliminator));
std::string name = line.substr(deliminator + 1);
bool available = true;
if (appid) {
PRINT_DEBUG("Adding DLC: %u|%s| %u", appid, name.c_str(), available);
settings_client->addDLC(appid, name, available);
settings_server->addDLC(appid, name, available);
}
}
}
} else {
//unlock all DLC
PRINT_DEBUG("Unlocking all DLC");
bool unlock_all = ini.GetBoolValue("app::dlcs", unlock_all_key, true);
if (unlock_all) {
PRINT_DEBUG("unlocking all DLCs");
settings_client->unlockAllDLC(true);
settings_server->unlockAllDLC(true);
} else {
PRINT_DEBUG("locking all DLCs");
settings_client->unlockAllDLC(false);
settings_server->unlockAllDLC(false);
}
std::list<CSimpleIniA::Entry> dlcs_keys{};
if (!ini.GetAllKeys("app::dlcs", dlcs_keys) || dlcs_keys.empty()) return;
// remove the unlock all key so we can iterate through the DLCs
dlcs_keys.remove_if([](const CSimpleIniA::Entry &item){
return common_helpers::str_cmp_insensitive(item.pItem, unlock_all_key);
});
for (const auto &dlc_key : dlcs_keys) {
AppId_t appid = (AppId_t)std::stoul(dlc_key.pItem);
if (!appid) continue;
auto name = ini.GetValue("app::dlcs", dlc_key.pItem, "unknown DLC");
PRINT_DEBUG("adding DLC: [%u] = '%s'", appid, name);
settings_client->addDLC(appid, name, true);
settings_server->addDLC(appid, name, true);
}
}

View File

@ -56,22 +56,6 @@ Check the example file in `steam_settings.EXAMPLE\configs.EXAMPLE.ini`.
---
## DLC:
By default the emulator will try to unlock all DLCs (by returning `true` when the game calls the `BIsDlcInstalled()` function).
If the game uses the other function you will need to provide a list of DLCs to the emulator.
To do this first create a `steam_settings` folder right beside the emulator.
In this folder, put a `DLC.txt` file. (path will be `<path where the emu lib is>\steam_settings\DLC.txt`)
If the DLC file is present, the emulator will only unlock the DLCs in that file. If the file is empty all DLCs will be locked.
The contents of this file are:
`appid=DLC name`
See the `steam_settings.EXAMPLE` folder for an example.
---
## Languages:
You can include a `steam_settings\supported_languages.txt` file with a list of languages that the game supports.

View File

@ -37,6 +37,16 @@ inventory=STEAMINVENTORY_INTERFACE_V001
video=STEAMVIDEO_INTERFACE_V001
masterserver_updater=SteamMasterServerUpdater001
[app::dlcs]
# should the emu report all DLCs as unlocked
# some games check for "hidden" DLCs, hence this should be set to 1 in that case
# but other games detect emus by querying for a fake/bad DLC, hence this should be set to 0 in that case
# default=1
unlock_all=0
# format: ID=name
1234=DLCNAME
56789=This is another example DLC name
[app::paths]
556760=../DLCRoot0
1234=./folder_where_steam_api_is

View File

@ -317,7 +317,7 @@ EXTRA_FEATURES_CONVENIENT = {
},
'configs.overlay.ini': {
'overlay::general': {
'enable_experimental_overlay': (1, 'xxx USE AT YOUR OWN RISK xxx, enable the experimental overlay'),
'enable_experimental_overlay': (1, 'XXX USE AT YOUR OWN RISK XXX, enable the experimental overlay, might cause crashes or other problems'),
'disable_warning_any': (1, 'disable any warning in the overlay'),
},
}
@ -565,11 +565,12 @@ def write_ini_file(base_path: str, out_ini: dict):
for file in out_ini.items():
with open(os.path.join(base_path, file[0]), 'wt', encoding='utf-8') as f:
for item in file[1].items():
f.write('[' + item[0] + ']\n') # section
f.write('[' + str(item[0]) + ']\n') # section
for kv in item[1].items():
f.write('# ' + kv[1][1] + '\n') # comment
f.write(kv[0] + '=' + str(kv[1][0]) + '\n') # key/value pair
f.write('\n') # key/value pair
if kv[1][1]: # comment
f.write('# ' + str(kv[1][1]) + '\n')
f.write(str(kv[0]) + '=' + str(kv[1][0]) + '\n') # key/value pair
f.write('\n')
def main():
USERNAME = ""
@ -832,6 +833,8 @@ def main():
}
}
})
# write the data as soon as possible in case a later step caused an exception
write_ini_file(emu_settings_dir, out_config_app_ini)
dlc_config_list : list[tuple[int, str]] = []
dlc_list, depot_app_list, all_depots = get_dlc(game_info)
@ -850,12 +853,25 @@ def main():
dlc_config_list.append((dlc, dlc_name))
# we create the DLC fle nonetheless, empty file makes the emu lock DLCs, otherwise everything is allowed
# we set unlock_all=0 nonetheless, to make the emu lock DLCs, otherwise everything is allowed
# some games use that as a detection mechanism
with open(os.path.join(emu_settings_dir, "DLC.txt"), 'wt', encoding="utf-8") as f:
if dlc_config_list:
merge_dict(out_config_app_ini, {
'configs.app.ini': {
'app::dlcs': {
'unlock_all': (0, 'should the emu report all DLCs as unlocked, default=1'),
}
}
})
for x in dlc_config_list:
f.write(f"{x[0]}={x[1]}\n")
merge_dict(out_config_app_ini, {
'configs.app.ini': {
'app::dlcs': {
x[0]: (x[1], ''),
}
}
})
# write the data as soon as possible in case a later step caused an exception
write_ini_file(emu_settings_dir, out_config_app_ini)
if all_depots:
with open(os.path.join(emu_settings_dir, "depots.txt"), 'wt', encoding="utf-8") as f:
@ -945,15 +961,11 @@ def main():
logo,
logo_small)
out_config_main_ini = {}
if DISABLE_EXTRA:
merge_dict(out_config_main_ini, EXTRA_FEATURES_DISABLE)
merge_dict(out_config_app_ini, EXTRA_FEATURES_DISABLE)
if CONVENIENT_EXTRA:
merge_dict(out_config_main_ini, EXTRA_FEATURES_CONVENIENT)
if out_config_main_ini:
write_ini_file(emu_settings_dir, out_config_main_ini)
merge_dict(out_config_app_ini, EXTRA_FEATURES_CONVENIENT)
if out_config_app_ini:
write_ini_file(emu_settings_dir, out_config_app_ini)

View File

@ -0,0 +1,52 @@
## Save & Settings folder migration tool
This tool allows you to migrate your `steam_settings` folder, or your global `settings` folder from the old format, to the new `.ini` oriented format.
## Where is the global settings folder?
On Windows this folder is located at `%appdata%\Goldberg SteamEmu Saves\settings\`
On Linux this folder is located at:
* if env var `XDG_DATA_HOME` is defined:
`$XDG_DATA_HOME/Goldberg SteamEmu Saves/settings/`
* Otherwise, if env var `HOME` is defined:
`$HOME/.local/share/Goldberg SteamEmu Saves/settings/`
## How to migrate the global settings folder
Simply open the terminal/cmd in the folder of the tool and run it **without** any arguments.
* On Windows:
```shell
migrate_gse.exe
```
* On Linux:
```shell
chmod 777 migrate_gse
./migrate_gse
```
The tool will generate a new folder in the current directory called `steam_settings`, copy the **content inside** this folder (a bunch of `.ini` files) and paste them here:
* On Windows: `%appdata%\GSE Saves\settings\`
* On Linux:
* if env var `XDG_DATA_HOME` is defined:
`$XDG_DATA_HOME/GSE Saves/settings/`
* Otherwise, if env var `HOME` is defined:
`$HOME/.local/share/GSE Saves/settings/`
Notice the new name of the global saves folder `GSE Saves`, not to be confused with the old one `Goldberg SteamEmu Saves`
## How to migrate a local `steam_settings` folder you already have
Open the terminal/cmd in the folder of the tool and run it with only one argument, which is the path to the target `steam_settings` folder.
* On Windows:
```shell
migrate_gse.exe "D:\my games\some game\steam_settings"
```
* On Linux (notice how the tilde character is outside the quotes to allow it to expand):
```shell
chmod 777 migrate_gse
./migrate_gse ~/"some game/steam_settings"
```
The tool will generate a new folder in the current directory called `steam_settings`, copy the **content inside** this folder (a bunch of `.ini` files) and paste them inside the target/old `steam_settings` folder
## General notes
* In all cases, the tool will not copy the achievements images, overlay fonts/sounds, and all the extra stuff, it will just generate the corresponding `.ini` files
* Some configuration files are still using the same old format, that includes all the `.json` files, depots.txt, subscribed_groups_xxx.txt, etc...
So don't just remove everything from the old `steam_settings` folder

View File

@ -29,12 +29,12 @@ def write_ini_file(base_path: str, out_ini: dict):
for file in out_ini.items():
with open(os.path.join(base_path, file[0]), 'wt', encoding='utf-8') as f:
for item in file[1].items():
f.write('[' + item[0] + ']\n') # section
f.write('[' + str(item[0]) + ']\n') # section
for kv in item[1].items():
if kv[1][1]:
f.write('# ' + kv[1][1] + '\n') # comment
f.write(kv[0] + '=' + str(kv[1][0]) + '\n') # key/value pair
f.write('\n') # key/value pair
if kv[1][1]: # comment
f.write('# ' + str(kv[1][1]) + '\n')
f.write(str(kv[0]) + '=' + str(kv[1][0]) + '\n') # key/value pair
f.write('\n')
itf_patts = [
( r'SteamClient\d+', "client" ),
@ -99,28 +99,22 @@ def main():
global_settings = os.path.join(home_env, 'Goldberg SteamEmu Saves', 'settings')
if not global_settings or not os.path.isdir(global_settings):
print('failed to detect settings folder', file=sys.stderr)
print('failed to detect folder', file=sys.stderr)
help()
sys.exit(1)
print(f'searching inside the folder: "{global_settings}"')
out_dict_ini = {}
# oh no, they're too many!
for file in glob.glob('*.*', root_dir=global_settings):
file = file.lower()
if file == 'force_account_name.txt':
if file == 'force_account_name.txt' or file == 'account_name.txt':
with open(os.path.join(global_settings, file), "r", encoding='utf-8') as fr:
merge_dict(out_dict_ini, {
'configs.user.ini': {
'user::general': {
'account_name': (fr.readline(), 'user account name'),
},
}
})
elif file == 'account_name.txt':
with open(os.path.join(global_settings, file), "r", encoding='utf-8') as fr:
merge_dict(out_dict_ini, {
'configs.user.ini': {
'user::general': {
'account_name': (fr.readline(), 'user account name'),
'account_name': (fr.readline().strip('\n').strip('\r'), 'user account name'),
},
}
})
@ -129,11 +123,11 @@ def main():
merge_dict(out_dict_ini, {
'configs.app.ini': {
'app::general': {
'branch_name': (fr.readline(), 'the name of the beta branch'),
'branch_name': (fr.readline().strip(), 'the name of the beta branch'),
},
}
})
elif file == 'force_language.txt':
elif file == 'force_language.txt' or file == 'language.txt':
with open(os.path.join(global_settings, file), "r", encoding='utf-8') as fr:
merge_dict(out_dict_ini, {
'configs.user.ini': {
@ -142,16 +136,7 @@ def main():
},
}
})
elif file == 'language.txt':
with open(os.path.join(global_settings, file), "r", encoding='utf-8') as fr:
merge_dict(out_dict_ini, {
'configs.user.ini': {
'user::general': {
'language': (fr.readline().strip(), 'the language reported to the app/game, https://partner.steamgames.com/doc/store/localization/languages'),
},
}
})
elif file == 'force_listen_port.txt':
elif file == 'force_listen_port.txt' or file == 'listen_port.txt':
with open(os.path.join(global_settings, file), "r", encoding='utf-8') as fr:
merge_dict(out_dict_ini, {
'configs.main.ini': {
@ -160,25 +145,7 @@ def main():
},
}
})
elif file == 'listen_port.txt':
with open(os.path.join(global_settings, file), "r", encoding='utf-8') as fr:
merge_dict(out_dict_ini, {
'configs.main.ini': {
'main::connectivity': {
'listen_port': (fr.readline().strip(), 'change the UDP/TCP port the emulator listens on'),
},
}
})
elif file == 'force_steamid.txt':
with open(os.path.join(global_settings, file), "r", encoding='utf-8') as fr:
merge_dict(out_dict_ini, {
'configs.user.ini': {
'user::general': {
'account_steamid': (fr.readline().strip(), 'Steam64 format'),
},
}
})
elif file == 'user_steam_id.txt':
elif file == 'force_steamid.txt' or file == 'user_steam_id.txt':
with open(os.path.join(global_settings, file), "r", encoding='utf-8') as fr:
merge_dict(out_dict_ini, {
'configs.user.ini': {
@ -272,17 +239,20 @@ def main():
},
}
})
elif file == 'enable_experimental_overlay.txt':
elif file == 'enable_experimental_overlay.txt' or file == 'disable_overlay.txt':
enable_ovl = 0
if file == 'enable_experimental_overlay.txt':
enable_ovl = 1
merge_dict(out_dict_ini, {
'configs.overlay.ini': {
'overlay::general': {
'enable_experimental_overlay': (1, 'XXX USE AT YOUR OWN RISK XXX, enable the experimental overlay, might cause crashes'),
'enable_experimental_overlay': (enable_ovl, 'XXX USE AT YOUR OWN RISK XXX, enable the experimental overlay, might cause crashes'),
},
}
})
elif file == 'app_paths.txt':
with open(os.path.join(global_settings, file), "r", encoding='utf-8') as fr:
app_lines = [lll for lll in fr.readlines() if lll.strip()]
app_lines = [lll.strip('\n').strip('\r') for lll in fr.readlines() if lll.strip() and lll.strip()[0] != '#']
for app_lll in app_lines:
[apppid, appppath] = app_lll.split('=', 1)
merge_dict(out_dict_ini, {
@ -292,6 +262,25 @@ def main():
},
}
})
elif file == 'dlc.txt':
with open(os.path.join(global_settings, file), "r", encoding='utf-8') as fr:
dlc_lines = [lll.strip('\n').strip('\r') for lll in fr.readlines() if lll.strip() and lll.strip()[0] != '#']
merge_dict(out_dict_ini, {
'configs.app.ini': {
'app::dlcs': {
'unlock_all': (0, 'should the emu report all DLCs as unlocked, default=1'),
},
}
})
for dlc_lll in dlc_lines:
[dlcid, dlcname] = dlc_lll.split('=', 1)
merge_dict(out_dict_ini, {
'configs.app.ini': {
'app::dlcs': {
dlcid.strip(): (dlcname, ''),
},
}
})
elif file == 'achievements_bypass.txt':
merge_dict(out_dict_ini, {
'configs.main.ini': {
@ -305,7 +294,7 @@ def main():
merge_dict(out_dict_ini, {
'configs.main.ini': {
'main::general': {
'crash_printer_location': (fr.readline(), 'this is intended to debug some annoying scenarios, and best used with the debug build'),
'crash_printer_location': (fr.readline().strip('\n').strip('\r'), 'this is intended to debug some annoying scenarios, and best used with the debug build'),
},
}
})
@ -467,6 +456,7 @@ def main():
os.makedirs('steam_settings')
write_ini_file('steam_settings', out_dict_ini)
print(f'new settings written inside: "{os.path.join(os.path.curdir, "steam_settings")}"')
if __name__ == "__main__":