EvenLess wrote: ↑Fri Aug 04, 2023 11:42 pm
- 2023-08-20:
- Another list (.xlsx/Excel), available as a [Link]. It's even too large for Google Spreadsheets to import it.
It contains ALL "GameObjects" found in ALL RootTemplates, with ALL attributes. 25139 rows, and more columns (attributes) I care to count (A to QT). A lot of these columns/attributes are probably irellevant, but it was easier to just extract all than try to figure out what to filter.
The PowerShell script I wrote to do this, are inserted below. It took 30 minutes to complete on my system.
Code: Select all
$Stopwatch = New-Object -TypeName System.Diagnostics.Stopwatch
$Stopwatch.Start()
$UnpackedDataFolder = [System.IO.FileInfo]'C:\Games\BG3-Tools\BG3-Modders-Multitool\UnpackedData'
$MyDocuments = [System.IO.FileInfo][System.Environment]::GetFolderPath('MyDocuments')
$CheatTableFolder = [System.IO.FileInfo]"$($MyDocuments.FullName)\My Cheat Tables\bg3"
$LocalizationPath = [System.IO.FileInfo]"$($UnpackedDataFolder.FullName)\English\Localization\English\english.xml"
$CsvPath = "$($CheatTableFolder)\all_templates.csv"
$LocalizationData = New-Object -TypeName System.Xml.XmlDocument
$LocalizationData.PreserveWhitespace = $true
$LocalizationData.Load($LocalizationPath.FullName)
$RootTemplateFolders = @(
"$($UnpackedDataFolder.FullName)\Shared\Public\Shared\RootTemplates"
,"$($UnpackedDataFolder.FullName)\Shared\Public\SharedDev\RootTemplates"
,"$($UnpackedDataFolder.FullName)\Gustav\Public\Gustav\RootTemplates"
,"$($UnpackedDataFolder.FullName)\Gustav\Public\GustavDev\RootTemplates"
)
$RootTemplates = Get-ChildItem -Recurse -File -Filter *.lsx -Path $RootTemplateFolders
# Empty array to contain the objects.
$Objects = @()
# Empty array to contain the attributes.
$Attributes = @()
foreach ($Template in $RootTemplates) {
$Object = [ordered]@{}
$XmlDocument = New-Object -TypeName System.Xml.XmlDocument
$XmlDocument.PreserveWhitespace = $true
$XmlDocument.Load($Template.FullName)
$Nodes = $XmlDocument.SelectNodes("//node[@id='GameObjects']")
if ($Nodes.Count -eq 0) { continue }
$Nodes.attribute | ForEach-Object {
$k = $_.id
$v = $_.value
if ($k -notin $Attributes) {
$Attributes += $k
}
$Object.Add($k, $v)
if ($_.type -eq 'TranslatedString') {
$k = "$($k)English"
$v = $_.handle
if ($k -notin $Attributes) {
$Attributes += $k
}
$Object.Add("$($k)English", $LocalizationData.SelectSingleNode("//content[@contentuid='$($v)']").'#text')
}
}
$Objects += New-Object -TypeName PSCustomObject -Property $Object
}
# All data are gathered, but we need to create a new object, where every entry contains all the same attribute/property keys, otherwise exporting to Csv apparently doesn't work very well.
# Empty array to contain the objects.
$oArray = @()
foreach ($o in $Objects) {
# Create/reset the hashtable used to all the attributes/properties.
# The ones explicitly listed, are so they are sorted first (i.e. the first columnds).
$oTable = [ordered]@{
Type = ''
MapKey = ''
ParentTemplateId = ''
Name = ''
DisplayNameEnglish = ''
}
# Sort the collected attributes and add the rest to the hashtable.
$Attributes | Sort-Object -Unique | ForEach-Object {
if ($_ -notin $oTable.Keys) {
$oTable.Add($_, '')
}
}
# Loop through all the attributes/properties.
foreach ($k in $o.psobject.Properties.Name) {
# Update the matching keys in the hashtable, with the collected value.
$oTable."$($k)" = $o."$($k)"
}
# Add the table to the array.
$oArray += New-Object -TypeName PSCustomObject -Property $oTable
}
$oArray | Export-Csv -NoTypeInformation -Force -Encoding UTF8 -Delimiter ';' -Path $CsvPath
$Stopwatch.Stop()
$Stopwatch.Elapsed
- 2023-08-19:
- Created new items list, available as a [Link] (too large to attach without compressing it).
It contains ALL of type=item
found in the RootTemplates, as well as various attributes for the items, that can be used for searching/filtering.
- 2023-08-19:
- Added
bg3_EvenLess_2023.08.19.CT
.
Minor fixes. Addes scrolls.
- Added
bg3_EvenLess_Scrolls_20230819T0856582171.CT
.
Just the scrolls. For merging with other tables.
- Added
bg3_scrolls.txt
.
CSV with the scrolls, with names.
- 2023-08-18:
- Added
bg3_EvenLess_2023.08.18.CT
.
- 2023-08-13:
- Added
bg3_items_20230813T0228529556.txt
. Removed bg3_items_20230812T2222384844.txt
.
Also [Link].
- 2023-08-12:
- Added
bg3_EvenLess_armor_v2.CT
.
Based on a new PowerShell script (which I'm refining), that automatically indexes all the items in the armor.txt-files, attempts to find the equipment slot. Looks up the localized name and description and adds this for the name and extra details in the script comments, then generates the final Cheat Table.
- Added
bg3_items_20230812T2222384844.txt
.
More details CSV with all armors in Armor.txt-files.
I have added a LOT of items to the Item Spawner. Primarily
Legendary and
Very Rare, and some
Rare and
Uncommon. I have sorted and color-coded them.
All this is in the attached
bg3_items_spells.CT
table, which is based
Zanzer's bg3.CT v4.1.1.3624901 (2) table.
You can also use one of the attached tables that only contains the items (
bg3_armor.CT
,
bg3_weapon.CT
, and
bg3_object.CT
). They contain a LOT more items, but are not as pretty and sorted as the ones I manually added in the
bg3_items_spells.CT
table.
To use these you must load
Zanzer's bg3.CT table first, then and Activate Console Commands, then Register Commands, before being able to use the Item Spawner.
Then from the already opened Cheat Engine, with
Zanzer's bg3.CT loaded, open my
bg3_armor.CT
, or
bg3_weapon.CT
, or
bg3_object.CT
and merge it into
Zanzer's bg3.CT.
These (incomplete) lists are based on all the items found in the
armor.txt
,
weapon.txt
, and
armor.txt
files, found in the following unpacked folders, and a list of those are added in the
bg_items.txt
(CSV) file:
- Gustav\Public\Gustav\Stats\Generated\Data
- Gustav\Public\GustavDev\Stats\Generated\Data
- Shared\Public\Shared\Stats\Generated\Data
- Shared\Public\SharedDev\Stats\Generated\Data
I've attached another, more complete and more searchable list, as
bg3_items_v2.txt
(CSV) file. This one contains all items found in the all the RootTemplates-files. This does include a lot of items that cannot be added to inventory, from the looks of it. It does not contain all files, I've discovered, but it is the most complete list so far.
If you want to find more items yourself, I created a small guide in another post, but have added it here as well, to have all the tidbits gathered in one post. The guide is based on the
[Link] (which
CKeylos also pointed out.
- Download and extract the latest version of Norbyte's ExportTool [Link].
- Download and extract the latest version of ShinyHobo's BG3-Modders-Multitool [Link].
I extracted the BG3-Modders-Multitool to the base directory of Norbyte's ExportTool, as the Multitool requires divine.exe
from this.
- Open the multitool (
bg3-modders-multitool.exe
).
- Add the paths to
divine.exe
and bg3.exe
by clicking the asterisk in the lower right hand corner after opening the Multitool.
- Unpack .pak Files and select at least
Gustav.pak
and Shared.pak
. I recommend also unpacking English.pak
as this contains the translations you see in-game.
This step takes a while and takes up a around 30 GB of disk space.
- Index Files. This step also takes a while.
- Index Search to search for things. If you search for the in-game name, you should get results from
English.loca
. The line here contains a "handle" (ID). Use this handle to do a new search, and you will find the files that contain references to this handle. These files likely contains the UUID/MapKey.
- For more control/advanced use, use the
ConverterApp.exe
to batch convert all .lsf-files (binary) to .lsj-files (json) to make them more readable. Also use ConvertApp.exe
to convert English.loca
(binary) to xml.
The way I created the lists and the item Cheat Tables, was with a few PowerShell scripts I wrote.
The first script, which only inventories the before-mentioned .txt-files, is pretty fast to run. It creates both the CSV and the 3 item Cheat Tables. Remember to update paths to your locations.
Code: Select all
# Get the path for the My Documents folder.
$DocumentsFolder = [System.Environment]::GetFolderPath('MyDocuments')
# The default location where Cheat Engine saves cheat tables.
$CheatTablesFolder = Join-Path -Path $DocumentsFolder -ChildPath 'My Cheat Tables'
$ItemsCsvFile = 'bg3_items.csv'
$ItemsCsvPath = Join-Path -Path $CheatTablesFolder -ChildPath $ItemsCsvFile
$ItemsCtFile = 'bg3_armor.CT'
$ItemsCtPath = Join-Path -Path $CheatTablesFolder -ChildPath $ItemsCtFile
# Base-path of the unpacked data/.pak-files.
$BasePath = 'C:\Games\BG3-Tools\UnpackedData'
# Get tools to extract with here:
# https://github.com/Norbyte/lslib/releases/latest
# https://github.com/ShinyHobo/BG3-Modders-Multitool/releases/latest
# Sub-paths to the armor, object, and weapon .txt-files.
$ArmorFiles = @(
'Shared\Public\Shared\Stats\Generated\Data\Armor.txt'
,'Shared\Public\SharedDev\Stats\Generated\Data\Armor.txt'
,'Gustav\Public\Gustav\Stats\Generated\Data\Armor.txt'
,'Gustav\Public\GustavDev\Stats\Generated\Data\Armor.txt'
)
$WeaponFiles = @(
'Shared\Public\Shared\Stats\Generated\Data\Weapon.txt'
,'Shared\Public\SharedDev\Stats\Generated\Data\Weapon.txt'
,'Gustav\Public\Gustav\Stats\Generated\Data\Weapon.txt'
,'Gustav\Public\GustavDev\Stats\Generated\Data\Weapon.txt'
)
$ObjectFiles = @(
'Shared\Public\Shared\Stats\Generated\Data\Object.txt'
,'Shared\Public\SharedDev\Stats\Generated\Data\Object.txt'
,'Gustav\Public\Gustav\Stats\Generated\Data\Object.txt'
,'Gustav\Public\GustavDev\Stats\Generated\Data\Object.txt'
)
# Combine to one.
#$Files = $ArmorFiles + $WeaponFiles + $ObjectFiles
$Files = $ArmorFiles + $WeaponFiles + $ObjectFiles
$RarityColor = @{
'RARITY_UNKNOWN' = 'C0C0C0'
'Uncommon' = '00FF00'
'Rare' = 'FFFF00'
'VeryRare' = 'FF00FF'
'Legendary' = '4080FF'
}
$CheatTableHead = @"
<?xml version="1.0" encoding="utf-8"?>
<CheatTable CheatEngineTableVersion="45">
<CheatEntries>
<CheatEntry>
<ID>@@CE_ID@@</ID>
<Description>"Item Spawner"</Description>
<Options moHideChildren="1"/>
<GroupHeader>1</GroupHeader>
<CheatEntries>
"@
$CheatTableFoot = @"
</CheatEntries>
</CheatEntry>
</CheatEntries>
<UserdefinedSymbols>
<SymbolEntry>
<Name>playerCharactersPtr</Name>
<Address>1D4526F0000</Address>
</SymbolEntry>
</UserdefinedSymbols>
<DisassemblerComments>
<DisassemblerComment>
<Address>"bg3.exe"+25C0F20</Address>
<Comment>Zanzer</Comment>
</DisassemblerComment>
</DisassemblerComments>
</CheatTable>
"@
$CheatEntryTemplate = @"
<CheatEntry>
<ID>@@CE_ID@@</ID>
<Description>"@@ITEM_NAME@@"</Description>
<Color>@@RARITY_COLOR@@</Color>
<VariableType>Auto Assembler Script</VariableType>
<AssemblerScript Async="1">[ENABLE]
{`$lua}
if syntaxcheck then return end
local uuid = "@@ITEM_UUID@@"
local cmdCall = getAddress("cmdCall")
local cmdAddr = getAddress("cmdAddr")
local cmdArgs = getAddress("cmdArgs")
local cmdStr1 = getAddress("cmdStr1")
PrepareCall("GetHostCharacter")
executeCodeEx(0, nil, cmdCall)
writePointer(cmdArgs + 0x18, readPointer(cmdArgs + 0x08))
PrepareCall("CreateAtObject")
writePointer(cmdArgs + 0x08, cmdStr1)
writeString(cmdStr1, uuid)
writeBytes(cmdStr1 + #uuid, 0)
writeQword(cmdArgs + 0x28, 0)
writeQword(cmdArgs + 0x38, 0)
writeQword(cmdArgs + 0x48, 0)
writeQword(cmdArgs + 0x58, 0)
executeCodeEx(0, nil, cmdCall)
{`$asm}
assert(true)
[DISABLE]
</AssemblerScript>
</CheatEntry>
"@
# Regular Expression to check if the UUID looks right.
$RegexUUID = [regex]'^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$'
# Create an empty array to store all the items.
$Items = ''
# Create an empty string variable to store the content of the files.
$Content = ''
# Loop through the files and read their contents into one big string.
foreach ($File in $Files) {
# Combine base-path and sub-path to the full path.
$FullPath = Join-Path -Path $BasePath -ChildPath $File
$Content += Get-Content -Raw -Path $FullPath
}
# Split the content string into an array of lines.
$Line = $Content -split '\n'
# Create an empty array to store the item objects in.
$Items = @()
# Ensure the Item variable is empty for the first iteration.
Clear-Variable -Name Item -ErrorAction SilentlyContinue
# Loop through all the lines.
for ($l = 0; $l -lt $Line.Count; $l++) {
# Check if the current line is the start of a new item entry.
if ($Line[$l] -match '^new entry "(?<Name>\w+)"') {
# Store the items "name" in a new variable, so it does not get overwritten by following mathes.
$ItemName = $Matches.Name
# Check if we already have an item object, and if that item object contains a valid UUID.
if ($Item -and $Item.UUID -match $RegexUUID) {
# Store the previous item object in the array.
$Items += New-Object -TypeName psobject -Property $Item
}
# Create a fresh hashtable and populate it with the default values.
$Item = [ordered]@{
Name = $ItemName
UUID = $null
Type = 'TYPE_UNKNOWN'
Rarity = 'RARITY_UNKNOWN'
}
}
else {
# Not a new item entry, so look for the current item entrys data.
# Check if the current line is the item type.
if ($Line[$l] -match 'type "(?<Type>\w+)"') {
# Update the hashtable with the found value.
$Item.Type = $Matches.Type
}
# Check if the current line is the item rarity.
if ($Line[$l] -match '^data "Rarity" "(?<Rarity>\w+)"') {
# Update the hashtable with the found value.
$Item.Rarity = $Matches.Rarity
}
# Check if the current line is the item UUID.
if ($Line[$l] -match '^data "RootTemplate" "(?<UUID>[a-f0-9-]+)"') {
# Update the hashtable with the found value.
$Item.UUID = $Matches.UUID
}
}
}
# Export the items to a the items CSV-file. Using semi-colon (;) as delimiter, simply because the CSV file will automatically be shown in colums, in spreadsheet programs.
$Items | Sort-Object -Property Name | Export-Csv -Force -NoTypeInformation -Encoding Default -Delimiter ';' -Path $ItemsCsvPath
# Build the Cheat Table.
foreach ($t in ($Items | Select-Object -ExpandProperty Type -Unique)) {
$CtFile = "bg3_$($t.ToLower()).CT"
$CtPath = Join-Path -Path $CheatTablesFolder -ChildPath $CtFile
$CheatID = 1
$CheatTableHead.Replace('@@CE_ID@@', $CheatID) | Out-File -Force -Encoding default -FilePath $CtPath
foreach ($i in ($Items | Where-Object { $_.Type -eq $t} | Sort-Object -Property Name)) {
$CheatID++
$CheatEntryTemplate.Replace('@@CE_ID@@', $CheatID).Replace('@@ITEM_NAME@@', $i.Name).Replace('@@RARITY_COLOR@@', $RarityColor."$($i.Rarity)").Replace('@@ITEM_UUID@@', $i.UUID) | Out-File -Append -Encoding default -FilePath $CtPath
}
$CheatTableFoot | Out-File -Append -Encoding default -FilePath $CtPath
}
The second script was an attempt to find ALL items, as it looked like all items were stored in the RootTemplates-files. I also wanted the localized names, so the script will also find that and add to the list. This script is VERY SLOW. First run (which generated the attached
bg3_items_v2.txt
-file) took more than 14 hours to complete (14 hours, 5 minutes, 2 seconds and 404 milliseconds to be exact).
It requires all the binary .lsf-files to be converted .lsj JSON-files. Easily done using the
ConverterApp.exe
that is included in Norbyte's ExportTool.
Code: Select all
$VerbosePreference = 'SilentlyContinue'
$VerbosePreference = 'Continue'
$Stopwatch = New-Object -TypeName System.Diagnostics.Stopwatch
$Stopwatch.Start()
$DocumentsFolder = [System.Environment]::GetFolderPath('MyDocuments')
$CheatTablesFolder = Join-Path -Path $DocumentsFolder -ChildPath 'My Cheat Tables'
$CsvFile = 'bg3_allitems.csv'
$CsvPath = Join-Path -Path $CheatTablesFolder -ChildPath $CsvFile
$BasePath = 'C:\Games\BG3-Tools\UnpackedData'
$LocalizationPath = "$($BasePath)\English\Localization\English\english.xml"
$RootTemplates = @(
"$($BasePath)\Shared\Public\Shared\RootTemplates"
,"$($BasePath)\Shared\Public\SharedDev\RootTemplates"
,"$($BasePath)\Gustav\Public\Gustav\RootTemplates"
,"$($BasePath)\Gustav\Public\GustavDev\RootTemplates"
)
$LocalizationData = [xml](Get-Content -Encoding Default -Path $LocalizationPath)
$RegexUUID = [regex]'^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$'
function Get-TemplateData {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[System.IO.FileInfo]
$Path
,
[Parameter(Mandatory = $true)]
[guid]
$Id
)
Write-Verbose -Message "Fn: Get-TemplateData -Id '$($Id)'"
$Template = Join-Path -Path $Path -ChildPath "$($Id).lsj"
$Success = $false
if (Test-Path -Path $Template) {
$Success = $true
try {
$Data = Get-Content -Raw -Encoding Default -Path $Template | ConvertFrom-Json
}
catch {
$Success = $false
}
}
if ($Success) {
return $Data.save.regions.Templates.GameObjects[0]
}
}
$Items = @()
$Files = Get-ChildItem -Path $RootTemplates -Filter *.lsj
Write-Output '"UUID";"Name";"Stats";"DisplayName";"Description"' | Out-File -Force -Encoding default -FilePath $CsvPath
foreach ($f in $Files) {
$TemplatePath = $f.Directory.FullName
$TemplateId = $f.BaseName
$Item = [ordered]@{
UUID = $f.BaseName
Name = $null
Stats = $null
DisplayName = $null
Description = $null
}
Write-Verbose -Message "L1: Read '$($f.BaseName)'."
do {
$Data = Get-TemplateData -Path $TemplatePath -Id $TemplateId
if ('Name' -notin $Data.psobject.Properties.Name) {
Write-Verbose -Message "L2: Name missing for '$($f.BaseName)'."
break
}
foreach ($p in $Data.psobject.Properties.Name) {
switch ($p) {
'Name' {
if ($null -eq $Item.Name) {
Write-Verbose -Message "L3: Adding Name '$($Data.Name.value)'."
$Item.Name = $Data.Name.value
}
}
'Stats' {
if ($null -eq $Item.Stats) {
Write-Verbose -Message "L3: Adding Stats '$($Data.Stats.value)'."
$Item.Stats = $Data.Stats.value
}
}
'DisplayName' {
if ($null -eq $Item.DisplayName) {
$LocalizedName = ($LocalizationData.contentList.content | Where-Object {
$_.contentuid -eq $Data.DisplayName.handle
}).'#text'
Write-Verbose -Message "L3: Adding DisplayName '$($LocalizedName)'."
$Item.DisplayName = $LocalizedName
}
}
'Description' {
if ($null -eq $Item.Description) {
$LocalizedName = ($LocalizationData.contentList.content | Where-Object {
$_.contentuid -eq $Data.Description.handle
}).'#text'
Write-Verbose -Message "L3: Adding Description '$($LocalizedName)'."
$Item.Description = $LocalizedName
}
}
}
}
$TemplateId = $Data.ParentTemplateId.value
} while ($Data.psobject.Properties.Name -contains 'ParentTemplateId' -and $Data.ParentTemplateId.value -match $RegexUUID)
Write-Verbose -Message "L1: Done '$($f.BaseName)'."
if ($null -eq $Item.DisplayName) {
Write-Verbose -Message "L1: DisplayName missing for '$($f.BaseName)'."
continue
}
$ItemObject = New-Object -TypeName psobject -Property $Item
$ItemObject | ConvertTo-Csv -NoTypeInformation -Delimiter ';' | Select-Object -Skip 1 | Out-File -Append -Encoding default -FilePath $CsvPath
#$Items += $ItemObject
}
#$Items | Export-Csv -Force -NoTypeInformation -Encoding Default -Delimiter ';' -Path $CsvPath
$Stopwatch.Stop()
$Stopwatch.Elapsed
Disclaimer!
Zanzer deserves all the credit for the Cheat Tables.