Okay, here it is, the costume exchange protocol.
First, some definitions.
COLOR: For the purposes of this document, COLOR is an 8-digit hexadecimal representation of a color. The order of the digits is RRGGBBAA. The numbers a-f must be represented in lowercase (important for hashing). The alpha channel MUST be ff in all cases; Paragon Chat enforces this by ORing the alpha with 0xff before hashing or sending the costume.
The alpha value is unused by the game, but the client sometimes sticks random numbers there that have no meaning. In hindsight, using 6 digits for the protocol would have been better, but it's too late now to change it without breaking things.
COSTUME HASH: A costume hash is a short (~28 byte) string that uniquely identifies a costume. These hashes are sent in presence stanzas. When a client needs a costume, it can use the hash to request a peer who owns that costume to send a copy. Once a client has received a costume, it should cache the costume locally for a reasonable length of time and associate it with the corresponding hash, so that it does not need to be requested again.
To compute the costume hash, first concatenate together the following into a single byte array or string*:
BodyType + SkinColor + Scale + BoneScale + HeadScale + ShoulderScale + ChestScale + WaistScale + HipScale + LegScale + Head3DScale + Brow3DScale + Cheek3DScale + Chin3DScale + Cranium3DScale + Jaw3DScale + Nose3DScale + Parts[]
BodyType is a string that is "0" for male, "1" for female, or "4" for huge.
SkinColor is a COLOR.
Each *Scale is a string representation of a floating point number for each of the scale sliders; as found in a costume file. They should be truncated to at most 4 decimal places before hashing and trailing zeroes removed. Scales which have a value of 0 are skipped and not concatenated.
Each *3DScale is an 8-digit hexadecimal number representing the encoded version of a 3-dimensional floating point vector. How to calculate this will be covered below. Encoded scales which have a value of 00000000 are skipped and not concatenated.
For each costume part, numbered 0 to 27 in the same order they appear in a saved costume file or demorecord, examine the Geo and Fx of the part. If they are both empty or "None", skip this costume part and do not hash it. Otherwise, concatenate the decimal string representation of the number (0-27) of the part to the string to be hashed. Then concatenate the Geometry, Texture1, Texture2, and Fx strings, in that order,
if they are not empty or 'None'.
For each Color1, Color2, Color3, or Color4 of a costume part that is not skipped, and where the color is not black (RGB = 000000), first concatenate either a "1", "2", "3", or "4" depending on which color it is, then the hex COLOR representation.
Once all of the valid parts are concatenated, take the SHA-1 of the combined string. Base64 encode the resulting hash. The Base64 result is the costume hash.
* Or use incremental SHA-1 updates, which is what PC does, but saying to concatenate it all is easier to understand for documentation purposes and has the same end result.
Costume request protocol:
Upon encountering an entity whose costume hash is known, but no valid costume is cached for, Paragon Chat sends a costume request to the peer in this form:
<iq to="user@domain/resource" id="id12345" type="get"><costume xmlns="pc:costume" hash="ZAqyuuB77cTBY/Z5p0b3q3+10fo="/></iq>
When such a request is received, if the hash corresponds to a known costume, Paragon Chat will reply with a copy of the costume in the following format:
<iq to="user@domain/resource" id="id12345" type="result">
<costume xmlns="pc:costume">
<appearance bodytype="1" skincolor="123456ff"
scale="2.1" bone="0.5" head="0.5"
shoulder="0.5" chest="0.5" waist="0.5" hip="0.5" leg="0.5"
head3d="123456ab" brow3d="123456ab" cheek3d="123456ab"
chin3d="123456ab" cranium3d="123456ab" jaw3d="123456ab"
nose3d="123456ab" />
<part n="0" geom="Leather_02"
tex1="Leather_03a" tex2="Leather_03b"
fx="Auras/Female/Gaseous/GasEyes.fx"
color1="123456ff" color2="123456ff"
color3="123456ff" color4="123456ff" />
<part n="1" ... />
...
</costume>
The rules for composing the stanza are identical to the rules for computing the hash. Scales with a value of 0.0 may be omitted from the appearance element. Encoded 3D scales with a value of 00000000 may also be omitted.
Costume parts that have neither a geom nor an fx (or where they are 'None') may be omitted. The "n" attribute is the decimal index of the costume part, from 0 to 27, so that the order can be reconstructed even if parts are omitted. geom, tex1, tex2, and fx attributes should be omitted if they are empty or 'None'. color* attributes should only be included if they have a value other than 000000ff.
3D scale encoding:
The three dimensional face scales are encoded in an optimized form comprised of a single 32-bit integer value rather that represents a 3-element floating point vector. This form comes from the COH network protocol and is how costumes are transmitted over the wire. It is only applicable to three-element vectors with each component having a value between -1.0 and 1.0.
The 32-bit integer is divided into 3 8-bit sections:
31 23 15 7 0
| | | | |
00000000 Szzzzzzz Syyyyyyyy Sxxxxxxxx
First, the value of each component (X,Y,Z) of the vector should be clamped to the range -1.0 to 1.0. It is multiplied by 100 and truncated, then encoded as an 8-bit integer with the 'S' acting as a sign bit. Bits 24-31 are unused and must be 0.
This encoding ensures that a vector of 0,0,0 is represented by an integer 0 so that it can be optimized out of transmission.