Project Perfect Mod Forums
:: Home :: Get Hosted :: PPM FAQ :: Forum FAQ :: Privacy Policy :: Search :: Memberlist :: Usergroups :: Register :: Profile :: Log in to check your private messages :: Log in ::


The time now is Thu Mar 28, 2024 7:16 pm
All times are UTC + 0
All the known information about PathMusic in RA3
Moderators: Global Moderators
Post new topic   Reply to topic Page 1 of 1 [3 Posts] Mark the topic unread ::  View previous topic :: View next topic
Author Message
jonwil
Rocket Infantry


Joined: 24 Jul 2005

PostPosted: Tue Apr 13, 2021 5:59 am    Post subject:  All the known information about PathMusic in RA3 Reply with quote  Mark this post and the followings unread

This post assumes you are familiar with modding RA3 (XML editing etc), with hexadecimal numbers and with C/C++ (including enums, structures and unions)

The attached zip contains various PathMusic related bits that I will reference later. You will also want to open up StaticStream.big from RA3 and extract a70bc1d6.9f12cf33.2d97c0ca.6d02956b.cdata as track.mus and a70bc1d6.9f12cf33.240021e2.dca9eaab.cdata as track-mem.mus

In Red Alert 3 there are 3 ways in which audio can be played. The first is when an AudioFile (or an AudioFileMP3Passthrough) is played directly. The second is when a specific PathEvent is triggered. And the third is when a PathMusicGameDynamicState is activated.

The following PathMusic related assets exist:
PathMusicMap (this points to the main PathMusic .mpf data file that gets loaded by the PathMusic system)
PathMusicTrack (this points to the PathMusic track .mus data files that hold the actual music)
PathMusicEvent (this matches the name of the event to the event ID)
PathMusicEventSet (this is used to create lists of PathMusicEvents including the list used for the "play event" script action)
PathMusicGameDynamicState (holds information about a path music dynamic state)
PathMusicGameDynamicStateSet (holds a list of PathMusicGameDynamicStates)
PathMusicGameDynamicTransition (is used to transition between PathMusic GameDynamicStates)
MusicScriptConditionNugget (there are a bunch of these and are used to determine when to transition between states)

The way that the PathMusicDynamicState system works is that when a state is activated, it triggers its EnterEvent. Then it checks each of its Transitions. If the transitions condition is matched, it transitions to the new state. When a state is deactivated, it triggers its ExitEvent.

The key to the whole system is the PathMusicEvent. As far as I have been able to tell, the only things the rest of the engine can do to the PathMusic system (the bit that handles the .mpf and .mus files) are to start an event playing, pause the currently playing event, stop the currently playing event and adjust the volume/gain/fade for the currently playing event (full details of that not yet known, need to track down the script action handler and find how all that works first)

In the zip file, RA3Music.h maps the name of a PathMusicEvent to its hash (e.g. BriefingTrack maps to 0x12671d4) and the name of a PathMusicTrack to its hash (e.g. Track maps to 0x11000001)

pc_pathmusicassets.xml contains definitions of the PathMusicMap and PathMusicTracks from RA3 (referencing the pc.mpf file, the track.mus file and the track-mem.mus file)

BasePathMusicEvent.xml and PathMusicEvents.xml contain definitions for the PathMusicEvents.

pathmusic.h contains definitions that will be referenced below when I talk about the format of the PathMusicMap file (pc.mpf) and the PathMusicTrack files (track.mus and track-mem.mus)

The .mpf file starts with a PATHFINDHEADER structure.
id will be 0x50464478, majorRev will be 5 and minorRev will be 3.

The nodeoffsets field of the PATHFINDHEADER structure points to a series of unsigned short values (with numnodes holding the count for this). Each value is multiplied by 4 to give an offset to a PATHFINDNODE structure.
The nodedata field of the PATHFINDHEADER structure points to the start of the area where all the PATHFINDNODE structures are.

Each PATHFINDNODE structure is followed by PATHFINDNODE.numbranches instances of a PATHFINDBRANCH structure.
The use of the other fields in the PATHFINDNODE structure (and all the fields in the PATHFINDBRANCH structure) is unknown at this time.

The eventoffsets field of the PATHFINDHEADER structure points to a series of unsigned short values (with numevents holding the count for this). Each value is multiplied by 4 to give an offset to a PATHEVENT structure.
The eventdata field of the PATHFINDHEADER structure points to the start of the area where all the PATHEVENT structures are.

Each PATHEVENT structure is followed by PATHEVENT.numactions instances of a PATHACTION structure.
The eventID field of the PATHEVENT structure represents the eventID. This will match to the IDs in ra3music.h except that the IDs in ra3music.h need to be ANDed with 0x00FFFFFF first (its unclear what the upper byte of the ra3music.h ID corresponds to)

The type field of the PATHACTION structure is an instance of the PATHACTIONTYPE enum and represents the action type.
The use of the other fields in the PATHFINDNODE and PATHACTION structures is unknown at this time.

The namedvars field of the PATHFINDHEADER points to a number of PATHNAMEDVAR structures (with numnamedvars holding the count for these). The use of the fields in the PATHNAMEDVAR structure is unknown at this time.

The noderouters field of the PATHFINDHEADER structure points to series of int values (with numrouters being the count for these). What these ints mean is unknown at this time.

The trackoffsets field of the PATHFINDHEADER structure points to a series of int values (with numtracks holding the count for this). Each value is multiplied by 4 to give an offset to a PATHTRACKINFO structure.
The trackinfos field of the PATHFINDHEADER structure points to the start of the area where all the PATHTRACKINFO structures are.

The muschecksum field in the PATHTRACKINFO structure matches with the checksum field in the PATHMUSHEADER structure. The use of the other fields in the PATHTRACKINFO structure is unknown at this time.

The sampleoffsets field in the PATHFINDHEADER structure points to a series of PATHFINDSAMPLE structures (the field that holds the count of these structures is unknown at this time).
The use of the fields in the PATHFINDSAMPLE structure is unknown at this time.

The use of the other fields in the PATHFINDHEADER structure is known at this time.

The .mus files start with a PATHMUSHEADER structure followed by PATHMUSHEADER.numsamples instances of a PATHNODEOFFSETS structure. The checksum field matches with the muschecksum field in the PATHTRACKINFO structure. The use of the other fields in the PATHMUSHEADER structure is unknown at this time.

The snroffset field in the PATHNODEOFFSETS structure (when multiplied by 16) points to data for a .snr file with the snrbytes field indicating how large it is. The snsoffset field in the PATHNODEOFFSETS structure (when multiplied by 128) points to data for a .sns file with the snsbytes field indicating how large it is.

The use of the other fields in the PATHNODEOFFSETS structure is unknown at this time.

You can find code that shows how to read and write snr and sns files at https://github.com/jonwil/snrtool and https://github.com/jonwil/lame-3.96
All the snr/sns files embedded in the PathMusic data of RA3 are version 0 SndPlayer files and all audio appears to be compressed with the ealayer3_int codec.

EDIT:
Forgot to attach the zip file Smile



pathmusic.zip
 Description:

Download
 Filename:  pathmusic.zip
 Filesize:  30.09 KB
 Downloaded:  71 Time(s)


Back to top
View user's profile Send private message
jonwil
Rocket Infantry


Joined: 24 Jul 2005

PostPosted: Fri Sep 24, 2021 10:52 pm    Post subject: Reply with quote  Mark this post and the followings unread

I have uploaded some source code and bits at
https://mega.nz/file/uslVyCZL#065MUmqicjhsc7CE5XFvpUHMI9Xp1QgFm4u5CCf_gHA
that shows exactly how to read in, parse and write out a .mpf file and a .mus file. Its capable of reading in those files and writing out byte-for-byte-identical files. Its got all the known information on the data structures used by the mpf and mus files, the padding/alignment bytes that get written out etc.

It also shows how to get a .snr and .sns file for each piece of audio in the .mus file.

I have code here
https://github.com/jonwil/snrtool
https://github.com/jonwil/lame-3.96
in snrtool.cpp that shows a way to convert a .snr/.sns file into a .wav file and how to convert a .wav file into a .snr and .sns file.

I am hoping that this information can be used to build a tool to work with this data (at the very least something that just displays all the data and lets you manipulate the numbers etc) and maybe eventually a full setup to work with it all.

Back to top
View user's profile Send private message
jonwil
Rocket Infantry


Joined: 24 Jul 2005

PostPosted: Fri Oct 08, 2021 11:35 pm    Post subject: Reply with quote  Mark this post and the followings unread

Here is what is known at this point about the different structures based on an analysis of the .mpf files from the PC versions of RA3, Uprising and C&C4. Fields labeled "always x" are fields where none of the mpf files we have contain a value other than the specified one.

A .mpf file starts with a PATHFINDHEADER structure. This is what is known about its fields:
id is always 0x50464478
majorRev is always 5
minorRev is always 3
release is always 0xB0
prerelease is always 3
saveIncrement is always 0
generateID is always 0
projectID is always 0
numtracks is the count of how many tracks there are in the mpf file
numsections is always 1
numevents is the count of how many events there are in the mpf file
numrouters is the count of how many routers there are in the mpf file
numnamedvars is the count of how many variables there are in the mpf file
numnodes is the count of how many nodes there are in the mpf file
nodeoffsets points to a series of unsigned short values. Each value is multiplied by 4 to give an offset to a PATHFINDNODE structure
nodedata points to the start of the area where all the PATHFINDNODE structures are
eventoffsets points to a series of unsigned short values. Each value is multiplied by 4 to give an offset to a PATHEVENT structure
eventdata points to the start of the area where all the PATHEVENT structures are
namedvars points to a number of PATHNAMEDVAR structures
noderouters points to a series of int values. Each value is multiplied by 4 to give an offset to a to a series of ints, the purpose of which is not known.
trackoffsets points to a series of int values. Each value is multiplied by 4 to give an offset to a PATHTRACKINFO structure
trackinfos points to the start of the area where all the PATHTRACKINFO structures are
sampleoffsets points to a series of PATHFINDSAMPLE structures
mapfilelen indicates the size of the data within the .mpf file
v40reserve is unused and is always 0

This is what is known about the fields in the PATHFINDNODE structure:
index indicates which sample this node is referencing
trackID indicates which track this node is referencing
sectionID is unknown
repeat can be -1, 0 or 1. The meaning of the values is unknown
routerID indicates which router this node is referencing
numbranches is how many branches (PATHFINDBRANCH structures) this node has
controller is always 0
beats is unknown
bars is  unknown
partID is unknown
synchevery is always 0
synchoffset is always 0
notes is unknown
synch is always 0
channelbranching is always 0
unused is always 0

All of the fields in the PATHNODEEXTRA structure (referenced as extra in the PATHFINDNODE structure) are always 0

A PATHFINDNODE structure is followed by zero or more PATHFINDBRANCH structures.
This is what is known about the fields in the PATHFINDBRANCH structure:
controlmin is the minimum value of the controller to test against
controlmax is the maximum value of the controller to test against
dstnode is the destination node for this branch

This is what is known about the fields in the PATHEVENT structure:
queued is always 0
expiry is always 0
lastact is always 0
eventID is the event ID for this event
numactions is the number of actions (PATHACTION structures) this event has
currentaction is always 0
voices is always 0
priority is always 0
bumplower is always 0
beingFiltered is always 0
project is always 0
unused is always 0

A PATHEVENT structure is followed by zero or more PATHACTION structures. This is what is known about the fields in the PATHACTION structure:
track indicates what track to use for this action
sectionID is always 0
type is of type PATHACTIONTYPE and identifies what type this action is
done is always 0
leftvaluetype is of type PATHVALUETYPE and identifies the type of the left value
rightvaluetype is of type PATHVALUETYPE and identifies the type of the right value
assess is used with PATHACTION_CONDITION and will be zero if PATHACTIONTYPE is not PATHACTION_CONDITION
comparison is of type PATHCOMPARETYPE and identifies the comparison type for PATHACTION_CONDITION. It will be PATH_COMPARE_INVALID if PATHACTIONTYPE is not PATHACTION_CONDITION
indent is always 0
unused is always 0

Here is how the different PATHACTION types work (the ones actually seen in the 3 games that is)
PATHACTION_SETVALUE is used to set a value:
leftvaluetype will either be PATH_VALUE_VARIABLE or PATH_VALUE_SPECIAL. If its a variable then PATHACTSETVALUE.setwhat will be the variable ID, otherwise it will be PATH_VOLUME or PATH_CONTROLLER
rightvaluetype will either be PATH_VALUE_INTEGER or PATH_VALUE_SPECIAL. If its an integer then PATHACTSETVALUE.towhat will be the value to use otherwise it will be PATH_RANDOMSHORT

PATHACTION_CALC is used to modify a value:
leftvaluetype will be PATH_VALUE_VARIABLE and rightvaluetype will be PATH_VALUE_INTEGER. PATHACTCALC.value is the variable to change. PATHACTCALC.op is the operator to use (of type PATHOPERATOR) and PATHACTCALC.by is the value to apply.

PATHACTION_EVENT triggers an event:
leftvaluetype will be PATH_VALUE_INTEGER and rightvaluetype will be PATH_VALUE_BADTYPE. PATHACTEVENT.eventid will be the ID of an event.

PATHACTION_WAITTIME triggers a wait:
leftvaluetype will be PATH_VALUE_BADTYPE.
If rightvaluetype is PATH_VALUE_INTEGER, PATHACTWAITTIME.millisecs will be the time in milliseconds to wait. If rightvaluetype is PATH_VALUE_SPECIAL, PATHACTWAITTIME.millisecs will be PATH_TIMETONEXTBEAT, PATH_TIMETONEXTBAR or PATH_TIMETONEXTNODE. PATHACTWAITTIME.lowest is always 0

PATHACTION_FADE triggers a fade:
leftvaluetype will be PATH_VALUE_BADTYPE.
rightvaluetype will be PATH_VALUE_INTEGER.
PATHACTFADE.tovol is the volume to fade to.
PATHACTFADE.id is the type of fade (of type PATHFADETYPE)
PATHACTFADE.flip is always 0
PATHACTFADE.ms is the time in ms for the fade.

PATHACTION_CONDITION triggers a condition check:
What happens depends on the value of PATHACTION.assess.
If assess is 1 or 2, leftvaluetype will be PATH_VALUE_VARIABLE, rightvaluetype will be PATH_VALUE_INTEGER, PATHACTION.comparison will be set to a comparison type, PATHACTCONDITION.value will be the variable to compare against and PATHACTCONDITION.compareValue will be the value to compare to. Its unclear what the difference between assess 1 and assess 2.
If assess is 3, leftvaluetype and rightvaluetype will be PATH_VALUE_BADTYPE, PATHACTION.comparison will be PATH_COMPARE_INVALID, PATHACTCONDITION.value will be 0 and PATHACTCONDITION.compareValue will be 0.
If assess is 4, leftvaluetype and rightvaluetype will be PATH_VALUE_BADTYPE, PATHACTION.comparison will be PATH_COMPARE_INVALID, PATHACTCONDITION.value will be 0 and PATHACTCONDITION.compareValue will be 0.

It is not known yet exactly how the code decides what to do after its evaluated a PATH_ACTION_CONDITION and which action it evaluates next.

PATH_ACTION_BRANCHTO triggers a branch to a node:
There are 2 cases, the first has leftvaluetype and rightvaluetype set to PATH_VALUE_INTEGER with PATHACTBRANCHTO.node, PATHACTBRANCHTO.offsection and PATHACTBRANCHTO.immediate all set to -1.
The second has leftvaluetype set to PATH_VALUE_INTEGER, rightvaluetype set to PATH_VALUE_BADTYPE, PATHACTBRANCHTO.node set to a node value and PATHACTBRANCHTO.offsection set to -1.

Its not known yet exactly how the code decides what nodes to trigger in what order (it involves the values from PATH_ACTION_BRANCHTO and PATHACTBRANCHTO, some of the fields in the PATHFINDNODE structure and the data in the PATHFINDBRANCH structures. (and quite possibly the data pointed to by noderouters as well).

This is what is known about the fields in the PATHNAMEDVAR structure:
name is the name of the variable
value is always 0

This is what is known about the fields in the PATHTRACKINFO structure:
startingsample indicates which entry in the sample array is the first one for this track.
numsubbanks is always 0
purgremode is always 0
muschecksum matches the checksum field in the PATHMUSHEADER structure
maxaram is unknown
maxmram is always 0

This is what is known about the fields in the PATHFINDSAMPLE structure:
offset is an offset into the mus file
duration is a duration for the sample

Its not known yet how the offset field is used to figure out which piece of audio in the .mus file to play.

The format of the .mus files is as follows:
The file starts with a PATHMUSHEADER structure.
This is what is known about the fields in the PATHMUSHEADER structure:
checksum is some kind of checksum of the file, it appears to only be used to link it to the muschecksum field in the PATHTRACKINFO structure and isn't actually verified by the game.
numsamples is the number of samples (i.e. separate bits of audio) are in the file.
reserved is unknown

Then after the header is a number of PATHNODEOFFSETS structures.
This is what is known about the fields in the PATHNODEOFFSETS structure:
nodechecksum is unknown
index is always 0
subindex is always 0
snroffset is multiplied by 16 to give the offset within the file of the .snr data
snsoffset is multiplied by 128 to give the offset within the file of the .sns data
snrbytes is the size of the data pointed to by snroffset
snsbytes is the size of the data pointed to by snsbytes
gstream is always 0

After the PATHNODEOFFSETS structures comes all the data for the snr files then the data for the sns files.

Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic Page 1 of 1 [3 Posts] Mark the topic unread ::  View previous topic :: View next topic
 
Share on TwitterShare on FacebookShare on Google+Share on DiggShare on RedditShare on PInterestShare on Del.icio.usShare on Stumble Upon
Quick Reply
Username:


If you are visually impaired or cannot otherwise answer the challenges below please contact the Administrator for help.


Write only two of the following words separated by a sharp: Brotherhood, unity, peace! 

 
You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Powered by phpBB © phpBB Group

[ Time: 0.1941s ][ Queries: 13 (0.0085s) ][ Debug on ]