Unpacked Loadout
This lists the format of the binary data used by an unpacked loadout.
For the location of folder containing unpacked loadout, see the Locations page.
Item | Path | Description |
---|---|---|
Header | header.bin |
Header with current loadout pointers. Facilitates 'transactions'. |
Events | events.bin |
List of all emitted events in the loadout. |
Timestamps | timestamps.bin |
Timestamps for each commit. |
Commit Parameters | commit-parameter-types.bin + commit-parameter-lengths-{x}.bin + commit-parameters-{x}.bin |
List of commit message parameters for each event. |
Configs | config.bin + config-data.bin |
Package Configurations. |
External Configs | external-config.bin + external-config-data.bin + external-config-paths.bin |
Package Configurations. |
Package Reference (IDs) | package-ids.bin |
Hashes of package IDs in this loadout. |
Package Reference (Versions) | package-versions-len.bin + package-versions.bin |
String versions of package IDs in this loadout. |
Store Manifests | stores.bin + store-data.bin |
Game store specific info to restore game to last version if possible. |
Commandline Parameters | commandline-parameter-data.bin |
Raw data for commandline parameters. Length specified in event. |
These files are deliberately set up in such a way that making a change in a loadout means appending to the existing files. No data is overwritten. Rolling back in turn means truncating the files to the desired length.
In some cases, data is grouped to improve compression ratios by bundling similar data together when sharing.
And in other cases, we put cold data that is infrequently accessed, e.g. commit message
params in
a separate file as that information is rarely accessed.
All values are in little endian unless specified otherwise.
They are shown in lowest to highest bit order.
So an order like u8
, and u24
means 0:8 bits, then 8:32 bits.
header.bin
This is a master file which tracks the state of other files in the loadout.
This stores the version of the loadout and structure counts for remainder of the loadout files.
In the event of an unexpected crash, this file is used to determine the last state of the Loadout before performing a cleanup of unused data (by truncating remaining files).
Format:
Data Type | Name | Description |
---|---|---|
u16 |
Version | Version of the loadout format. |
u16 |
Reserved | |
u32 |
NumEvents | Total number of events and timestamps in this loadout. |
u32 |
NumPackageIds | Total number of unique package ID(s) files in this loadout. |
u32 |
NumPackageVersions | Total number of unique package version(s) files in this loadout. |
u32 |
NumConfigs | Total number of package configuration files in this loadout. |
u32 |
NumGameVersions | Total number of game versions (store entries). |
u32 |
NumExternalConfigs | Total number of external configuration files in this loadout. |
Backwards compatibility is supported but not forwards.
If you're loading a Version
that is newer than what you support, you should reject the file
to avoid errors.
events.bin
This file contains all of the events that occurred in this loadout.
Each event has a 1:1 mapping to a timestamp in timestamps.bin. The number of events stored here is stored in header.bin.
The event format is documented in the Event List Page.
As a summary. Each event is composed of an u8
EventType and 0, 8, 24 or 56 bits of InlineData
(depending on EventType
). Events are laid out such that they align with 8 byte boundaries.
Any data that doesn't fit in the InlineData
field is stored in another file and loaded by index.
Details of that can be seen on each individual event entry.
Optimizing Events
Sometimes events can be optimized.
For example, if a package is added and then immediately enabled, we can cancel out the events.
As the nature of the events is such that they are always appended, we don't do this during normal operation. However, when we pack the loadout we will run certain clever optimizations like this to reduce clutter and save space.
Situations where optimizations are applied at pack stage will be noted in the event's description.
timestamps.bin
This contains the timestamp for each event.
Each timestamp here corresponds to an event in events.bin.
This is an array of 32-bit timestamps (R3TimeStamp[]
). The number of items is defined in
header.bin.
config.bin
This stores all historical package configurations for any point in time.
This is the array of file sizes, each being:
Data Type | Name | Description |
---|---|---|
u16 |
FileSize | Size of the configuration file. |
Every new config is appended to config-data.bin as it is added.
Each unique config has an index, a.k.a. ConfigIdx, which is an incrementing value from 0 every time a config is added. Emitted events refer to this index.
How do you use this data?
When loading a loadout, calculate the offsets of each config in memory, by iterating through the
FileSize
field(s).
- First config is at 0
- Second is at 0 +
FileSize(first)
- Third is at
second
+FileSize(second)
.
etc.
As you do this, hash the configs. For this, the snapshots use XXH3. When a new config is created, hash it and check if it's a duplicate, if it isn't, add it to the config list.
config-data.bin
This is a buffer of raw, unmodified, unique configuration files.
You can get the file size and offsets from the config.bin file.
external-config.bin
This stores all historical external configurations for any point in time.
External configurations are these generated by tools, as opposed to the ones generated by R3.
This is the array of file sizes, each being:
Data Type | Name | Description |
---|---|---|
u32 |
FileSize | Size of the configuration file. |
Every new config is appended to external-config-data.bin as it is added.
The path of every config is appended to external-config-paths.bin.
Each unique config has an index, a.k.a. ConfigIdx, which is an incrementing value from 0 every time a config is added. Emitted events refer to this index.
How do you use this data?
See: config.bin's How do you use this data?
section.
external-config-data.bin
This is a buffer of raw, unmodified, unique configuration files.
You can get the file size and offsets from the config.bin file.
external-config-paths.bin
This is a buffer of paths to configuration files generated by external tools/packages/programs.
This is a list of unique String8
s (1 byte prefixed UTF-8 Strings).
Each path is a path relative to the path specified in Config File Paths.
This is best illustrated with an example.
For a config with the path set to
[[ConfigFiles]]
Id = 3
Type = "Folder" # or 'ByExtension
Description = "OS-specific data folder"
[[ConfigFiles.Paths]]
OS = "any"
Path = "{LocalAppData}/ToolName/Data"
Storing a path of config.json
in external-config-paths.bin
would equal the final path to be:
"{LocalAppData}/ToolName/Data/config.json"
If the Type
is File
. An empty path would be used instead.
Packages
We refer to a 'unique package' by XXH3(PackageID)
The field PackageID
being the Id
field from Package.toml.
There can only be one version of a package with given ID in a loadout at a given time.
References to Package IDs (XXH3(PackageID)
) are referred to by an index known as
PackageIdIdx in the events:
- A
PackageIdIdx == 1
in an event meansfetch the entry at index 1
of package-ids.bin.
Most events will only require a PackageIdIdx. However, in some cases the version PackageVerIdx is also needed, for example, to upgrade packages.
As for how to use the data, it is similar to config.bin. We deduplicate entries by in-memory hash. So an event can always refer to a PackageIdIdx created in an earlier event to save space.
Launcher MUST ensure each published mod has valid update/download data.
Otherwise this system could fail, as a hash of packageID on its own is not useful.
package-ids.bin
This is a buffer of XXH3(PackageID)
Each entry is 8 bytes long.
Using a 64-bit hash, we need around 5 billion hashes until we reach a 50% chance of collision, that's quite plenty!
System can still always fail, we just pray it won't.
Some Numbers
Nexus Mods alone hosts 815999 mods as of 30th of May 2024 (obtained via GraphQL API).
The probability of a hash collision on whole mod set is roughly the following:
>>> r=815999
>>> N=2**64
>>> ratio = 2*N / r**2
>>> ratio
55407743.67551148
>>> 1-math.exp(-1/ratio)
1.8048018635141716e-08
That ends up being ~0.0000018% I'll be damned if R3 comes anywhere close to that.
Anyway, assuming a more modest '100000' mods will be made in R3's lifetime, we can expect a probability of 0.0000000027%, or more than 1 in 3.7 trillion.
If I'm ever that successful, I'd probably be funded enough that I could extend this to 128-bit hash, and at that point a meteor is more likely to land on your house (no joke).
This ID is used to restore the package.
Package Versions
In some contexts, it may also be useful to know the package version.
Reference to unique Package Version are referred to by an index known as PackageVerIdx in the events:
- So a
PackageVerIdx == 1
in an event meansfetch the entry at index 1
of package-versions.bin.
This information is sometimes used to e.g.
upgrade packages.
package-versions-len.bin
Contains the lengths of entries in package-versions.bin.
Data Type | Name | Description |
---|---|---|
u8 |
VersionLength | Size of the Version string. |
This data compresses extremely well.
Most versions are of form X.Y.Z
so there is a lot of repetition of 05
.
package-versions.bin
This is a buffer consisting of package versions, whose length is defined in package-reference.bin
These versions are stored as UTF-8 strings. No null terminator.
This data compresses extremely well.
Because the randomness (entropy) of values is low, the version components
are super commonly 1s and 0s, and almost always all first two numbers 0-1
and dot .
Restoring Actual Package Files
We follow a multi step process in order to reliably try restore Reloaded3 packages.
First we attempt to obtain full package metadata from Central Server.
But what if Central Server is down?
We will query the Static CDN API. That contains a dump of the latest package update info.
stores.bin
This stores all game store specific info.
Why do we store this info?
This info can be used to identify the game when you share the loadout with a friend, and the game isn't known by the Community Repository.
Or in the event that you cloud sync a game (between your machines) that's not known by the Community Repository.
It can also be used to identify when game updates have taken place when auditing the log.
Data Type | Name | Description |
---|---|---|
u8 (StoreType) |
StoreType | The store from which the game came from. |
u16 |
FileSize | Size of the configuration file. |
u8 |
Currently Unused |
The offsets can be derived from file sizes.
Basically this contains data specific to game stores such as GOG
, Steam
, Epic
etc. that can
be used to revert the game to an older version.
Reverting to earlier versions is not possible in all game stores.
store-data.bin
When values, e.g. strings are not available, they are encoded as 0 length strings, i.e. constant 00.
String8
is assumed to be a 1 byte length prefixed UTF-8 string.String16
is assumed to be a 2 byte length prefixed UTF-8 string.
CommmonData Struct
This struct is shared between all store entries.
i.e. This game was manually added.
Data Type | Name | Description |
---|---|---|
u64 |
ExeHash | The hash of the game executable (using (XXH3)) |
String16 |
ExePath | The path to the game executable |
String8 |
AppId | The application ID of the game |
We store this for every game, regardless of store.
Unknown
Data Type | Name | Description |
---|---|---|
u8 |
Version | The version of the structure |
CommonData |
CommonData | The common data structure |
Steam
Data Type | Name | Description |
---|---|---|
u8 |
Version | The version of the structure |
CommonData |
CommonData | The common data structure |
u64 |
AppId | The Steam application ID |
u64 |
DepotId | The Steam depot ID |
u64 |
ManifestId | The Steam manifest ID |
String8 |
Branch | The Steam branch name |
String8 |
BranchPassword | The password for the Steam branch (if password-protected) |
To perform rollback, will maintain basic minimal change fork of DepotDownloader
,
no need to reinvent wheel. Manifest contains SHA checksums and
all file paths, we might be able to only do partial downloads.
To determine current version, check the App's .acf
file in
steamapps
. The InstalledDepots
will give you the current
Depot
and Manifest
ID. Steam does not unfortunately have
user friendly version names.
To determine downloadable manifests, we'll probably have to use
SteamKit2
. Use DepotDownloader code for inspiration.
GOG
Extended details in Stores: GOG.
We can get the info from the registry at
HKEY_LOCAL_MACHINE\Software\GOG.com\Games\{GameId}
Data Type | Name | Description |
---|---|---|
u8 |
Version | The version of the structure |
CommonData |
CommonData | The common data structure |
u64 |
GameId | The unique identifier for the game on GOG |
u64 |
BuildId | The unique identifier for the build |
String8 |
VersionName | The user-friendly version name for display purposes |
The VersionName
is also copied into the commit message on each update.
To identify the version reliably, it seems we will need to compare the hashes against the ones in the different depots.
This will also allow us to support e.g. Heroic on Linux.
Heroic & Playnite
These are 3rd party launchers that support GOG
They need to be supported, because there's no official Linux launcher.
TODO: To be determined.
Epic
Version downgrade with Epic isn't possible.
We will store the minimal amount of data required to identify the game in the hopes it is one day.
With Epic we can nip this data from C:\Program Data\Epic\EpicGamesLauncher\Data\Manifests
.
We want the following:
Data Type | Name | Description |
---|---|---|
u8 |
Version | The version of the structure |
CommonData |
CommonData | The common data structure |
u128 |
CatalogItemId | The MD5 hash identifier for the game on Epic |
String8 |
AppVersionString | The version string of the game on Epic |
These values are directly extracted from the manifest file.
Microsoft
Version downgrade with Microsoft isn't possible.
We will store the minimal amount of data required to identify the game in the hopes it is one day.
We're interested in AppXManifest.xml
in this case.
Data Type | Name | Description |
---|---|---|
u8 |
Version | The version of the structure |
CommonData |
CommonData | The common data structure |
String8 |
PackageFamilyName | The unique identifier for the game on the Microsoft Store. {Identity.Name}_{hash(Identity.Publisher)} |
String8 |
PackageVersion | The version of the game package on the Microsoft Store, from Identity field. |
The PackageVersion
is actually a four part version, but is stored as string, so just in case an invalid
version exists in some manifest, we will string it.
commandline-parameter-data.bin
This file contains the raw strings for commandline parameters. The lengths of the parameters are specified in the UpdateCommandline event.
Commit Parameters
These files contain the parameters for any event that requires additional info in its commit message.
The Commit Message file lists when messages appear in this file for each message.
When the message is not a contextual-parameter, it is stored in this file.
A timestamp is shown beside each event, it does not need to be embedded into description.
An Example
You emit the PackageStatusChanged event with the message commit-messages-packageadded:
Added '**{Name}**' with ID '**{ID}**' and version '**{Version}**'.
Which could be marked as:
Added '**Super Cool Mod**' with ID '**reloaded3.utility.somexample**' and version '**1.0.0**'
The Version
is a 'Contextual Parameter', and thus is derived from context.
It is not stored in the commit parameters.
Encoding
Parameters are encoded in the order in which they appear in the template!!
This would be encoded as:
-
commit-parameter-types.bin: [0, 0]
Explanation:
- 0: UTF-8 Char Array for "Super Cool Mod" (Name)
- 0: UTF-8 Char Array for "reloaded3.utility.somexample" (ID)
-
commit-parameters-lengths-8.bin: [14, 28]
Explanation:
- 14: Length of "Super Cool Mod"
- 28: Length of "reloaded3.utility.somexample"
-
Super Cool Mod
reloaded3.utility.somexample
These strings are written directly to the
commit-parameters-text.bin
file, without any null terminator or padding. -
commit-parameters-versions.bin: [0]
Explanation:
- 0: Version of the commit message.
It's 0 because this is the initial version of the message format.
With Back References
Suppose you wanted to repeat the earlier parameter, we would use back references.
-
commit-parameter-types.bin: [5, 5]
Explanation:
- 5: BackReference8 for
"Super Cool Mod"
- 5: BackReference8 for
"reloaded3.utility.somexample"
- 5: BackReference8 for
-
commit-parameters-backrefs-8.bin: [0, 1]
Explanation:
- 0: Index of
"Super Cool Mod"
- 1: Index of
"reloaded3.utility.somexample"
- 0: Index of
-
commit-parameters-versions.bin: [0]
Explanation:
- 0: Version of the commit message.
It's still 0 because we're using the same message format, just with back references.
With Back References (Optimized)
Suppose you have multiple parameters to backreference, there are optimized variants.
Let's say we want to reference all three parameters from the previous example in a new event:
-
commit-parameter-types.bin: [11]
Explanation:
- 11: BackReference3_8 for all three parameters
-
commit-parameters-backrefs-8.bin: [0, 1]
Explanation:
- 0: Index of
"Super Cool Mod"
- 1: Index of
"reloaded3.utility.somexample"
- 0: Index of
-
commit-parameters-versions.bin: [0]
Explanation:
- 0: Version of the commit message.
This optimized approach uses a single ParameterType (11: BackReference3_8
) to
reference all three parameters at once, reducing the overall size of the encoded data.
It's particularly efficient when you need to reference multiple consecutive parameters from a previous event.
Decoding
To construct commit messages from the unpacked loadout data, follow these steps.
-
Read Events Sequentially:
Process the events in events.bin in the order they appear. -
Determine Commit Message Type:
Based on the event type, identify the corresponding commit message template from Commit-Messages.md.
These are listed in the Event List page for each event under theMessages
section (Example) -
Check Message Version:
Read the version of the commit message from commit-parameters-versions.bin.
This ensures you're using the correct message format for that event type. -
Read and Process Parameters:
- Fetch the pre-parsed message template. (and number of parameters)
- Read the parameter types from commit-parameter-types.bin.
- Based on the parameter types, retrieve the actual parameter data from the appropriate locations:
- Contextual parameters like
EventTime
can be inferred from the event context. - Text data from commit-parameters-text.bin
- Timestamps from commit-parameters-timestamps.bin
- Back references from the appropriate commit-parameters-backrefs-*.bin file
- Lists from commit-parameters-lists.bin
- Contextual parameters like
-
Construct the Message: Use the template from step 2 and fill in the parameters obtained in steps 4 and 5.
commit-parameters-types.bin
This is an array of:
Data Type | Name | Description |
---|---|---|
u8 |
ParameterType | Type of the parameter. |
ParameterType
ParameterType
is defined as:
Type | Data Type | Example | Description |
---|---|---|---|
0 |
UTF-8 Char Array (u8 length) |
Hello, World! |
UTF-8 characters, length stored in commit-parameters-lengths-8.bin |
1 |
UTF-8 Char Array (u16 length) |
A longer string... |
UTF-8 characters, length stored in commit-parameters-lengths-16.bin |
2 |
UTF-8 Char Array (u32 length) |
An even longer string... |
UTF-8 characters, length stored in commit-parameters-lengths-32.bin |
3 |
u32 (R3TimeStamp) |
1st of January 2024 |
Renders as human readable time. |
4 |
u32 (R3TimeStamp) |
5 minutes ago |
Renders as relative time. |
5 |
u8 (BackReference8) |
Entry 1 | Reference to a single previous item. |
6 |
u16 (BackReference16) |
Entry 2 | Reference to a single previous item. |
7 |
u24 (BackReference24) |
Entry 3 | Reference to a single previous item. |
8 |
u32 (BackReference32) |
Entry 4 | Reference to a single previous item. |
9 |
variable List |
See Parameter Lists | Defines the start of a list. |
10 |
u8, u8 (BackReference2_8) |
Entries 1, 2 | Reference to two previous items, each index stored as u8. |
11 |
u8, u8, u8 (BackReference3_8) |
Entries 1, 2, 3 | Reference to three previous items, each index stored as u8. |
12 |
u16, u16 (BackReference2_16) |
Entries 1, 2 | Reference to two previous items, each index stored as u16. |
13 |
u16, u16, u16 (BackReference3_16) |
Entries 1, 2, 3 | Reference to three previous items, each index stored as u16. |
14 |
u24, u24 (BackReference2_24) |
Entries 1, 2 | Reference to two previous items, each index stored as u24. |
15 |
u24, u24, u24 (BackReference3_24) |
Entries 1, 2, 3 | Reference to three previous items, each index stored as u24. |
16 |
u32, u32 (BackReference2_32) |
Entries 1, 2 | Reference to two previous items, each index stored as u32. |
17 |
u32, u32, u32 (BackReference3_32) |
Entries 1, 2, 3 | Reference to three previous items, each index stored as u32. |
The parameter data is split into multiple files to aid compression:
- Text is expected to be mostly (English) ASCII and thus be mostly limited to a certain character set.
- Timestamps are expected to mostly be increasing.
- Other/Misc integers go in a separate file.
- Other/Misc floats go in a separate file.
Here is a listing of which parameter types go where:
Type | Data Type | File |
---|---|---|
0 |
UTF-8 Char Array (u8 length) |
commit-parameters-text.bin |
1 |
UTF-8 Char Array (u16 length) |
commit-parameters-text.bin |
2 |
UTF-8 Char Array (u32 length) |
commit-parameters-text.bin |
3 |
u32 (R3TimeStamp) |
commit-parameters-timestamps.bin |
4 |
u32 (R3TimeStamp) |
commit-parameters-timestamps.bin |
5 |
u8 (BackReference8) |
commit-parameters-backrefs-8.bin |
6 |
u16 (BackReference16) |
commit-parameters-backrefs-16.bin |
7 |
u24 (BackReference24) |
commit-parameters-backrefs-24.bin |
8 |
u32 (BackReference32) |
commit-parameters-backrefs-32.bin |
9 |
variable List |
commit-parameters-lists.bin |
10 |
u8, u8 (BackReference2_8) |
commit-parameters-backrefs-8.bin |
11 |
u8, u8, u8 (BackReference3_8) |
commit-parameters-backrefs-8.bin |
12 |
u16, u16 (BackReference2_16) |
commit-parameters-backrefs-16.bin |
13 |
u16, u16, u16 (BackReference3_16) |
commit-parameters-backrefs-16.bin |
14 |
u24, u24 (BackReference2_24) |
commit-parameters-backrefs-24.bin |
15 |
u24, u24, u24 (BackReference3_24) |
commit-parameters-backrefs-24.bin |
16 |
u32, u32 (BackReference2_32) |
commit-parameters-backrefs-32.bin |
17 |
u32, u32, u32 (BackReference3_32) |
commit-parameters-backrefs-32.bin |
Commit Parameter Lengths
The following files store the parameter lengths.
These files are only used whenever the used ParameterType requires it.
See the description
section of each ParameterType for more information.
commit-parameters-lengths-8.bin
This is an array of:
Data Type | Name | Description |
---|---|---|
u8 |
ParameterLength | Length of the parameter in bytes. |
commit-parameters-lengths-16.bin
This is an array of:
Data Type | Name | Description |
---|---|---|
u16 |
ParameterLength | Length of the parameter in bytes. |
commit-parameters-lengths-32.bin
This is an array of:
Data Type | Name | Description |
---|---|---|
u32 |
ParameterLength | Length of the parameter in bytes. |
commit-parameters-versions.bin
This enables versioning, ensuring that different variations of the same commit message can coexist.
There should be 1 entry for each event!! Regardless of whether it has a message or not!!
This is an array of:
Data Type | Name | Description |
---|---|---|
u8 |
Version | Version of the commit message. |
The version number corresponds to the version suffix in the message key.
For example:
- If the message key is
PACKAGE_ADDED_V0
, the version would be0
. - If the message key is
MOD_CONFIG_UPDATED_V1
, the version would be1
.
This array contains u8
values which correspond to the version of the commit message last issued
for each event.
For example, if the message for an event like PackageStatusChanged
is encoded with the key PACKAGE_ADDED_V0
, it would be written as 0
:
Added '**{Name}**' with ID '**{ID}**' and version '**{Version}**'.
However, if a new version of the message is introduced with a different meaning, order of
parameters, or number of parameters, it would use a new key like PACKAGE_ADDED_V1
, and
the version number would be 1
.
In practice, expect to see mostly 0
, as the text for most commit messages is unlikely to change
often. When changes are needed, a new version of the message is created with an incremented version
number in its key.
Compressing 1M zeroes with zstd yields file size of ~50 bytes.
Back References
Back References are a Special Type of Parameter that references a previous item.
Back References are used to deduplicate parameters.
The writer maintains a hash of all parameters so far and reuses the same parameter index if the parameter ends up being a duplicate.
This improves loadout sizes by reducing existing previous data.
Back References are defined as 1 or more ParameterIndex
fields, whose location and data type
depends on ParameterType.
A ParameterIndex
of 0
means 'the first commit parameter' in file.
1
means 'the second commit parameter' etc.
These are essentially indices into the commit-parameters-types.bin file.
In order to quickly handle back-references, the reader
should keep offsets of all parameters.
That is offsets in their perspective files, e.g. offsets into commit-parameters-text.bin
etc.
Parameter Lists
This primitive is used when you have an unknown number of items.
Imagine you have a message which says:
Changes were made, here they are:
{ChangeList}
And you want ChangeList
to have multiple items, so it could be something like:
Changes were made, here they are:
- Value **ResolutionX** changed to **1920**
- Value **ResolutionY** changed to **1080**
Where each localizable Change
item could be:
- Value **{Name}** changed to **{NewValue}**
This is where Parameter Lists
come in.
A Parameter List
is defined as:
Data Type | Name | Description |
---|---|---|
u8 |
ParameterType | Type of the parameter. |
u4 |
Version | [Event Specific] version of the list. |
u20 |
NumParameters | Number of parameters. |
For the example above, we can treat each Change
as 2 parameters.
In which case, if we had 2 changes, we would set NumParameters
to 4
.
The individual parameters for Name
and NewValue
would then follow
as regular parameters in Commit Parameters.
Why is there a Version
field?
Sometimes it may be desireable to change the structure.
Suppose you wanted to change Change
item to also have the previous value:
- Value **{Name}** changed from **{OldValue}** to **{NewValue}**
In order to perform this change, you would set the Version
field to 1
.
So when you read loadouts you can interpret both the old and new format side by side.
Message Template List
Find the full list of templates on the Commit Messages Page.