I got the script working but now having decode error py soundfx_search_and_update.py --events_to_lookup="./events_to_lookup.txt" --xml="./" --events_catalog="./eventsmetadata.json" --soundbanks="C:\Users\Administrator\Documents\ringtone\source\archive\base\sound\soundbanks" --audio_folder="./audiofolder"
Loading sfx_container.opusinfo... Done Loading cp_music.bnk.xml... Done Loading sfx_container.bnk.xml... Done Loading events_to_lookup.txt... Done Loading eventsmetadata.json... Traceback (most recent call last): File "D:\ModdingTools\SoundFX\soundfx_search_and_update.py", line 894, in <module> json_data = json.load(json_file) ^^^^^^^^^^^^^^^^^^^^ File "C:\Program Files\Python312\Lib\json\__init__.py", line 293, in load return loads(fp.read(), ^^^^^^^^^ UnicodeDecodeError: 'cp949' codec can't decode byte 0xc3 in position 4: illegal multibyte sequence
my events_to_lookup.txt is: ui_phone_sms ui_phone_01 ui_phone_initiation_call
I double click on the .py file and there is a black box for an instant but then dissapears, please make a full video instruction or a simpler tool
When I drag the PY script onto a CMD window it gives: File "F:\CP2077_MOD\soundfx_search_and_update.py", line 119 raise Exception(f'Unable to resolve element {node_type}[id={identifier}] from parent {parent_node.get('na')}') ^^ SyntaxError: f-string: unmatched '('
Hey, like I said below to another comment you should use this tool as a regular command-line tool. Open a "cmd.exe" window then enter the command from the tool's folder. The first line is to get into the folder and the second line calls the tool with arguments.
dir "/path/to/SoundFX Search and Update/" py soundfx_search_and_update.py --events_catalog="path/to/file" --xml="path/to/folder" --events_to_lookup="path/to/file"... and so on with the other arguments you want to use.
Can this tool convert the WEM files to english, that's what I need, something that will convert them to their actual sound file names, e.g. take "0001.WEM" and convert it to q307_sc_01_av_door_open_ext.wav or something. Essentially convert the wwiseId or the wems into something that is either the NAMED sound file, or that tells me which sound name is which WEM file.
Can this tool do that? Don't want to waste time if it can't make the sounds a readable format.
PS D:\ModdingTools\SoundFX Search and Update> py .\soundfx_search_and_update.py Traceback (most recent call last): File "D:\ModdingTools\SoundFX Search and Update\soundfx_search_and_update.py", line 3, in <module> import jsonpath_ng.ext as jp ModuleNotFoundError: No module named 'jsonpath_ng' PS D:\ModdingTools\SoundFX Search and Update>
I did generate them according to the description page, however it seemed that the python script still failed in using the generated XML files - it only uses the XML files that you uploaded on Nexus. I am just informing you of what is happening.
Apparently you did not exactly generate the XML as needed. I have just generated, tested and uploaded the files for v2.12 you can download them from the optional files section.
Sorry, really new to python. I can't get this to work. I installed and set up everything correctly, that I am sure of, but I don't know how to specify the paths to the required files. I tried scouring the code for any clues as to how to troubleshoot it, and when I ran it in IDLE, it gave me this:
Missing parameter for argument: --events_catalog Missing parameter for argument: --xml Missing parameter for argument: --events_to_lookup
I suspect the answer may be revealed with the additional argument help cmd prompt, but alas... I can't get that to work either. lol SMH
This is a simple command line tool. Just like you would run an EXE file with arguments. You must call it this way either from a command prompt window of from a IDE with run configurations:
py soundfx_search_and_update.py --events_catalog="path/to/file" --xml="path/to/folder" --events_to_lookup="path/to/file"... and so on.
Into an IDE you would generally have a text field to write all arguments at once: --events_catalog="path/to/file" --xml="path/to/folder" --events_to_lookup="path/to/file"...
And another box or text field to select the Python script.
Wow ok i got it. Kudos for the quick response! Thank you! And yes, I do feel stupid now; absolutely did not put quotation marks around any of the file paths...
Sir or ma'am, you put up a donate button RIGHT... NOW! ...Either that or give me your PayPal. You deserve something more than my undying gratitude. You just opened a whole other universe to me.
Haha thank you for your message ! My rewards are people satisfied with my mods. :)
Other than Donation Points on Nexusmods that may allow me to buy a key for Phantom Liberty some day; I don't have anything for donation. I don't really care to get more money doing mods. But thank you again. =D
Whenever I try to update OPUSpaks and OPUSINFO with my modified wav file, I get this error:
"Error during conversion of D:\CP77 Modding tools\SoundFX Search and Update\audio folder\728608401.wav into OPUS: b'Error parsing input file: D:\\CP77 Modding tools\\SoundFX Search and Update\\audio folder\\728608401.wav\r\n'
This only happens with the modified .wav file, running this with the original .wav file works fine. Does the audio file have to be the same length as the original, or have to adhere to a file size limit?
YES THAT WAS IT! Thanks Yeah, I exported it from FL Studio as an MP3 and then just renamed it to a WAV to save file space as it's a long file I just remade it and it worked fine :D
I'm trying to read your script to do things a bit more manually, but I'm a bit confused still.
Basically, I had this goal - find the tracks for Roach Race game.
So I managed to extract eventsmetadata.json file, that has this wwise id for mus_roach_race_kaer_morhen track: 1425589070.
Then I extracted cp_music.bnk into xml and found this action id 920637579 that matches wwise id 1425589070 (it's listed as CAkEvent).
I found also something related as idExt = 2080174084
How do I go from 920637579 or 2080174084 to finding correct wem or opuspak file? I.e. where is that mapping stored or how do I calculate it (I didn't get that from your script yet).
I had some problems with running it (I'm on Linux). Plus I'm interested in understanding how to calculate the name of the file given just the source of the game resources.
What do you mean by xpath queries, something defined in the XML?
I guess I can reconstruct it backwards. For example I found one of the wem files in the XML:
<fld ty="tid" na="sourceID" va="1009571067"/>
So they do reference actual IDs. But how does it work in case of opuspak's for example?
I see, thanks. Going backwards I see that both wem ids and opuspaks ids are referenced in the XML, so it must be connected there. I'll dig a bit more into XML itself.
Though I'm not sure if those ids are actually opuspaks ones.
I guess I need to understand better the chain of id identifiers to trace.
Like I got that idExt, but I'm not sure what to do with it.
nodes = parent_node.findall("./obj[@na='EventInitialValues']/lst[@na='actions']/obj[@na='Action']/fld[@na='ulActionID']") if not nodes: print(f'CAkEvent[id={identifier}] has an empty list of actions, therefore the event has no audio file associated') return {0}
for node in nodes: source_ids.update(self.resolve_node(parent_node, node.get('va')))
return source_ids
So that means it will return id 920637579 I mentioned above. But there is no wem if like that. So it means it's one of the opuspaks? How do I match that to the correct one?
This tool shall definitely work with Linux as this is only python. Use arguments like this: - -events_catalog=/path/to/eventsmetadata.json.json With quotes around the path.
And the xml argument must be a folder containing xml files.
72 comments
py soundfx_search_and_update.py --events_to_lookup="./events_to_lookup.txt" --xml="./" --events_catalog="./eventsmetadata.json" --soundbanks="C:\Users\Administrator\Documents\ringtone\source\archive\base\sound\soundbanks" --audio_folder="./audiofolder"
Loading sfx_container.opusinfo... Done
Loading cp_music.bnk.xml... Done
Loading sfx_container.bnk.xml... Done
Loading events_to_lookup.txt... Done
Loading eventsmetadata.json... Traceback (most recent call last):
File "D:\ModdingTools\SoundFX\soundfx_search_and_update.py", line 894, in <module>
json_data = json.load(json_file)
^^^^^^^^^^^^^^^^^^^^
File "C:\Program Files\Python312\Lib\json\__init__.py", line 293, in load
return loads(fp.read(),
^^^^^^^^^
UnicodeDecodeError: 'cp949' codec can't decode byte 0xc3 in position 4: illegal multibyte sequence
my events_to_lookup.txt is:
ui_phone_sms
ui_phone_01
ui_phone_initiation_call
When I drag the PY script onto a CMD window it gives:
File "F:\CP2077_MOD\soundfx_search_and_update.py", line 119
raise Exception(f'Unable to resolve element {node_type}[id={identifier}] from parent {parent_node.get('na')}')
^^
SyntaxError: f-string: unmatched '('
dir "/path/to/SoundFX Search and Update/"
py soundfx_search_and_update.py --events_catalog="path/to/file" --xml="path/to/folder" --events_to_lookup="path/to/file"... and so on with the other arguments you want to use.
Can this tool do that? Don't want to waste time if it can't make the sounds a readable format.
You have the correspondence of sourceID to event names in the console output and in the report file if you use the --report argument.
PS D:\ModdingTools\SoundFX Search and Update> py .\soundfx_search_and_update.py
Traceback (most recent call last):
File "D:\ModdingTools\SoundFX Search and Update\soundfx_search_and_update.py", line 3, in <module>
import jsonpath_ng.ext as jp
ModuleNotFoundError: No module named 'jsonpath_ng'
PS D:\ModdingTools\SoundFX Search and Update>
I'll try to add json path at the end
i will achieve my goal
ok my friend, i managed to build wwiser gui
i'm following the instructions in your description
the road ahead is hard but i will climb to the top
and edited that whole length .wav with audio editing tool
now i only need to convert it to .opuspak
tried using opusenc.exe input.wav output.opuspak --serial 42 --quiet --padding 0 --vbr --comp 10 --framesize 20
(exactly the reverse process of opusdec.exe)
but when I pack it with wolvenkit the game doesn't read it.
D:\Program Files (x86)\GOG Galaxy\SoundFX Search and Update>python soundfx_search_and_update.py --events_catalog="eventsmetadata.json" --events_to_lookup="events_to_lookup.txt" --xml="D:\Program Files (x86)\GOG Galaxy\SoundFX Search and Update\xml"
Loading cp_music.bnk.xml... Done
Loading sfx_container.bnk.xml... Done
Loading events_to_lookup.txt... Done
Loading eventsmetadata.json... Done
Processing event 0%: ui_phone_sms
Traceback (most recent call last):
File "D:\Program Files (x86)\GOG Galaxy\SoundFX Search and Update\soundfx_search_and_update.py", line 945, in <module>
sourceIDs = last_used_parser.resolve_node(last_used_parser.tree.getroot(), event_wwise_id)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'resolve_node'
Don't have a clue in how to fix this..
EDIT: Fixed this by using your XML files, it seems the XML files exported from WolvenKit are causing issues with the tool.
Missing parameter for argument: --events_catalog
Missing parameter for argument: --xml
Missing parameter for argument: --events_to_lookup
I suspect the answer may be revealed with the additional argument help cmd prompt, but alas... I can't get that to work either. lol SMH
Any insight is appreciated.
You must call it this way either from a command prompt window of from a IDE with run configurations:
py soundfx_search_and_update.py --events_catalog="path/to/file" --xml="path/to/folder" --events_to_lookup="path/to/file"... and so on.
Into an IDE you would generally have a text field to write all arguments at once: --events_catalog="path/to/file" --xml="path/to/folder" --events_to_lookup="path/to/file"...
And another box or text field to select the Python script.
Other than Donation Points on Nexusmods that may allow me to buy a key for Phantom Liberty some day; I don't have anything for donation. I don't really care to get more money doing mods. But thank you again. =D
I'm glad you can have fun with this tool !
"Error during conversion of D:\CP77 Modding tools\SoundFX Search and Update\audio folder\728608401.wav into OPUS:
b'Error parsing input file: D:\\CP77 Modding tools\\SoundFX Search and Update\\audio folder\\728608401.wav\r\n'
This only happens with the modified .wav file, running this with the original .wav file works fine.
Does the audio file have to be the same length as the original, or have to adhere to a file size limit?
When I use Audacity to modify a WAV file I export it with the default settings.
Yeah, I exported it from FL Studio as an MP3 and then just renamed it to a WAV to save file space as it's a long file
I just remade it and it worked fine :D
Even if you rename the file into wav, the data is still mp3.
I’m glad you could solve your issue.
raise Exception(f'Unable to resolve element {node_type}[id={identifier}] from parent {parent_node.get('na')}')
^^
SyntaxError: f-string: unmatched '('
why this happend? using correct CMD.exe but error comes up
python istalled, pip and requirements all installed but that happen
Paste your full command line here so I can tell you if it is correct.
I'm trying to read your script to do things a bit more manually, but I'm a bit confused still.
Basically, I had this goal - find the tracks for Roach Race game.
So I managed to extract eventsmetadata.json file, that has this wwise id for mus_roach_race_kaer_morhen track: 1425589070.
Then I extracted cp_music.bnk into xml and found this action id 920637579 that matches wwise id 1425589070 (it's listed as CAkEvent).
I found also something related as idExt = 2080174084
How do I go from 920637579 or 2080174084 to finding correct wem or opuspak file? I.e. where is that mapping stored or how do I calculate it (I didn't get that from your script yet).
Thanks!
The tool will follow the links down to all source IDs (audio files).
If you look at xpath queries you will find the path. But why not just execute the tool ?
What do you mean by xpath queries, something defined in the XML?
I guess I can reconstruct it backwards. For example I found one of the wem files in the XML:
<fld ty="tid" na="sourceID" va="1009571067"/>
So they do reference actual IDs. But how does it work in case of opuspak's for example?
You need to understand Xpath but its not that difficult.
Though I'm not sure if those ids are actually opuspaks ones.
I guess I need to understand better the chain of id identifiers to trace.
Like I got that idExt, but I'm not sure what to do with it.
Otherwise it is a OPUS file contained into a Opuspak file. In this case you need to extract the OPUS data from the opuspak.
My tool does all this job too.
Maybe you can tell me what goes wrong on Linux when you run the tool ?
In JSON:
"redId": {
"$type": "CName",
"$storage": "string",
"$value": "mus_roach_race_kaer_morhen"
},
So I use: mus_roach_race_kaer_morhen which corresponds to
"wwiseId": 1425589070
So I look up that and get:
<obj na="CAkEvent" ix="13634">
<fld ty="u8" na="eHircType" va="4" vf="0x04 [Event]"/>
<fld ty="u32" na="dwSectionSize" va="9" vf="0x09"/>
<fld ty="sid" na="ulID" va="1425589070"/>
<obj na="EventInitialValues">
<fld ty="var" na="ulActionListSize" va="1"/>
<lst na="actions" co="1">
<obj na="Action" ix="0">
<fld ty="tid" na="ulActionID" va="920637579"/>
</obj>
</lst>
</obj>
</obj>
If I keep searching using let's say ulActionID: 920637579
I get a another node like
<obj na="CAkActionSetSwitch" ix="13633">
<fld ty="u8" na="eHircType" va="3" vf="0x03 [Action]"/>
<fld ty="u32" na="dwSectionSize" va="26" vf="0x1A"/>
<fld ty="sid" na="ulID" va="920637579"/>
<fld ty="u16" na="ulActionType" va="6401" vf="0x1901 [SetSwitch]"/>
<obj na="ActionInitialValues">
<fld ty="tid" na="idExt" va="2080174084"/>
<fld ty="u8" na="idExt_4" va="0" vf="0x00">
<fld ty="bit0" na="bIsBus" va="0"/>
</fld>
<obj na="AkPropBundle<AkPropValue,unsigned char>">
<fld ty="u8" na="cProps" va="1"/>
<lst na="pProps" co="1">
<obj na="AkPropBundle" ix="0">
<fld ty="u8" na="pID" va="15" vf="0x0F [DelayTime]"/>
<fld ty="uni" na="pValue" va="500"/>
</obj>
</lst>
</obj>
<obj na="AkPropBundle<RANGED_MODIFIERS<AkPropValue>>">
<fld ty="u8" na="cProps" va="0"/>
<lst na="pProps" co="0"/>
</obj>
<obj na="SwitchActionParams">
<fld ty="tid" na="ulSwitchGroupID" va="1338466608"/>
<fld ty="tid" na="ulSwitchStateID" va="2080174084"/>
</obj>
</obj>
</obj>
But I didn't get to soure ID, so I'm not sure I'm doing the right thing.
Simply look at them to understand the path.
python3 soundfx_search_and_update.py --events_catalog eventsmetadata.json.json --events_to_lookup events.txt --xml cp_music.bnk.xml
raise Exception(f'Unable to resolve element {node_type}[id={identifier}] from parent {parent_node.get('na')}')
^^
SyntaxError: f-string: unmatched '('
def find_CAkEvent_ulActionIDs(self, parent_node, identifier):
source_ids = set()
nodes = parent_node.findall("./obj[@na='EventInitialValues']/lst[@na='actions']/obj[@na='Action']/fld[@na='ulActionID']")
if not nodes:
print(f'CAkEvent[id={identifier}] has an empty list of actions, therefore the event has no audio file associated')
return {0}
for node in nodes:
source_ids.update(self.resolve_node(parent_node, node.get('va')))
return source_ids
So that means it will return id 920637579 I mentioned above. But there is no wem if like that. So it means it's one of the opuspaks? How do I match that to the correct one?
I'll try reading your opus related code.
Use arguments like this: - -events_catalog=/path/to/eventsmetadata.json.json
With quotes around the path.
And the xml argument must be a folder containing xml files.