Difference between revisions of "Splatoon 2/Save"

From Deep Sea Knowledge
Jump to navigation Jump to search
(Add missing base info)
m (SaveDataCmn)
 
(31 intermediate revisions by 5 users not shown)
Line 1: Line 1:
 +
{{WIP}}
 +
 
Splatoon 2 saves its data in '''<code>save.dat</code>'''.
 
Splatoon 2 saves its data in '''<code>save.dat</code>'''.
  
Line 4: Line 6:
  
 
* '''[https://leanny.github.io/SplatHeX.html SplatHeX]''': a full fledged save editor for Windows.
 
* '''[https://leanny.github.io/SplatHeX.html SplatHeX]''': a full fledged save editor for Windows.
* '''[https://github.com/3096/effective-spoon/releases/tag/rewrite Splatsave]''': an open source program that decrypts and re-encrypts <code>save.dat</code>.
+
* '''[https://github.com/3096/effective-spoon/releases Splatsave]''': an open source program that decrypts and re-encrypts <code>save.dat</code>.
  
 
== Revisions ==
 
== Revisions ==
  
Since release, there has been '''5''' revisions to this file:
+
Since release, there has been '''7''' revisions to this file:
  
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
|-
 
|-
! Ver
+
! Version
 
! Added On
 
! Added On
! Save Body Size
+
! Save Size
 
! Description
 
! Description
 
|-
 
|-
Line 28: Line 30:
 
|-
 
|-
 
| 2
 
| 2
| 2.3
+
| 2.3.0
 
| 0x86840
 
| 0x86840
 
| Added Stats Data (SaveDataStats)
 
| Added Stats Data (SaveDataStats)
Line 35: Line 37:
 
| 3.0.0
 
| 3.0.0
 
| 0x88D90
 
| 0x88D90
| Added Octo Expansion Data (SaveDataMsnOcta)
+
| Added Octo Expansion Data (SaveDataMsnOcta). Introduced new key look-up table.
 
|-
 
|-
 
| 4
 
| 4
 
| 4.0.0
 
| 4.0.0
 
| 0x88D90
 
| 0x88D90
| Introduced save body shuffle. Content is the same as Ver 3
+
| Introduced save body shuffle and new key look-up table. Content is the same as Ver 3.
 +
|-
 +
| 5
 +
| 4.2.0
 +
| 0x88D90
 +
| Same look-up table as Ver 4, but removed shuffling. Content is still the same as Ver 3.
 +
|-
 +
| 6
 +
| 4.3.0
 +
| 0x88D90
 +
| Internal version. Does not appear in actual saved files. Mostly likely it's the same encoding as before but converted added abilities.
 +
|-
 +
| 7
 +
| 4.3.0
 +
| 0x88D90
 +
| Introduced new look-up table and now shuffles twice, before and after encryption. Content is presumably still the same as Ver 3.
 +
|-
 +
| 8
 +
| 4.3.1
 +
| 0x88D90
 +
| Introduced additional encryption for each shuffle blocks. Shuffling now uses bit reversed/flipped CRC32 checksum as seed. Content is still the same as Ver 3.
 
|}
 
|}
  
Line 49: Line 71:
  
 
The encryption used on the save body is '''AES-128 CBC'''. The key is generated  
 
The encryption used on the save body is '''AES-128 CBC'''. The key is generated  
using a <code>sead::Random</code> object initialized from a 16 byte seed stored  
+
using a [[Sead/Pseudorandom Number Generator‎|sead::Random]] object initialized from a 16 byte seed stored  
 
in the save footer, in which also stores the initialization vector and the CMAC  
 
in the save footer, in which also stores the initialization vector and the CMAC  
 
for authentication. The CMAC key is generated from the same  
 
for authentication. The CMAC key is generated from the same  
<code>sead::Random</code> object after the encryption key.
+
<code>sead::Random</code> object after the encryption key. The key is generated
 +
by <code>Cmn::SaveDataFactory::Impl::IEncodedAllAccessor::createKey</code>.
  
The shuffling for save body takes place after it is encrypted. The game  
+
For version 4, a shuffling process for save body was added to take place after  
constructs a new <code>sead::Random</code> object initialized with the CRC32  
+
the save body gets encrypted. This shuffling is not present in version five,
from the save header. It then uses this to generate block sizes larger than  
+
but is done twice in version 7 and onward, with a shuffle before the body
 +
encryption as well. In save version 8, the shuffling doubles as an added layer
 +
of encryption. For determining the block sizes to shuffle, the game constructs
 +
a new <code>sead::Random</code> object. In version 4
 +
and 7, this <code>sead::Random</code> object is initialized simply with the  
 +
CRC32 checksum from the save header; in version 8, rather, the checksum is bit-
 +
reversed before being used for the after-encryption shuffle, and bit-reversed,
 +
plus bit-flipped for the before-encryption shuffle. The game then  
 +
uses the <code>sead::Random</code> object to generate block sizes larger than  
 
1/16 of body size and smaller than 1/8 of body size (or total remaining size,  
 
1/16 of body size and smaller than 1/8 of body size (or total remaining size,  
 
in case the remaining size is smaller than 1/8 of body size). When the  
 
in case the remaining size is smaller than 1/8 of body size). When the  
 
remaining size is smaller than 1/16 of body size, it is added as the size of  
 
remaining size is smaller than 1/16 of body size, it is added as the size of  
the last block. The game then uses <code>sead::PtrArrayImpl::shuffle</code>  
+
the last block. The body size for version 8, despite following the same
with the same <code>sead::Random</code> object to complete the shuffle.
+
general principal, goes an extra step to make sure that all the generated block
 +
sizes are multiple of 0x10, for the added "shuffle encryption" purpose. The
 +
game uses <code>sead::PtrArrayImpl::shuffle</code> with the same previous
 +
<code>sead::Random</code> object to complete the shuffle. While in version 4
 +
and 7, the shuffling is done by simply copying over the block buffers, the
 +
version 8 save creates more <code>sead::Random</code> objects for each block,
 +
initialized with the respective block size. The game uses these
 +
<code>sead::Random</code> objects to generate the IV used for block
 +
encryption, and then chains the object into
 +
<code>Cmn::SaveDataFactory::Impl::IEncodedAllAccessor::createKey</code>, the
 +
same key generation function used in "whole-body" encryption, to create the
 +
encryption key. Lastly, all the blocks are encrypted into their shuffled
 +
position with the existing AES-128 CBC algorithm.
  
== save.dat file structure ==
+
== File Structure ==
'''Save Header:'''
+
=== Header ===
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
|-
 
|-
Line 92: Line 135:
 
| 0
 
| 0
 
|}
 
|}
----
+
=== Body Structure ===
'''Save Body Structure'''
 
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
|-
 
|-
Line 143: Line 185:
 
| 0x867FC
 
| 0x867FC
 
| 0x8680C
 
| 0x8680C
|  
+
| 0x2544
 
| SaveDataMsnOcta
 
| SaveDataMsnOcta
 
|}
 
|}
----
+
=== SaveDataCmn ===
'''SaveDataCmn(0x10):'''
 
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
|-
 
|-
Line 159: Line 200:
 
| 0x10
 
| 0x10
 
| 4
 
| 4
| PlayerModelType (Gender, basically)
+
| Inkling PlayerModelType (Gender, basically)
| |0: Woomy | 1: Ngyes | 2: Veemo | 3: NO, 2 is better
+
| |0: Woomy | 1: Ngyes | 6: Story Octoling (Rev 0 and 1 only)
 
|-
 
|-
 
| 0x4
 
| 0x4
 
| 0x14
 
| 0x14
 
| 4
 
| 4
| PlayerHairId
+
| Inkling PlayerHairId
 
| See Mush/HairInfo.byml
 
| See Mush/HairInfo.byml
 
|-
 
|-
Line 171: Line 212:
 
| 0x18
 
| 0x18
 
| 4
 
| 4
| PlayerBottomId
+
| Inkling PlayerBottomId
 
| See Mush/BottomInfo.byml
 
| See Mush/BottomInfo.byml
 
|-
 
|-
Line 177: Line 218:
 
| 0x1C
 
| 0x1C
 
| 4
 
| 4
| PlayerSkinColorId
+
| Inkling PlayerSkinColorId
 
| 0 to 6, 0 being lightest and 6 darkest
 
| 0 to 6, 0 being lightest and 6 darkest
 
|-
 
|-
Line 183: Line 224:
 
| 0x20
 
| 0x20
 
| 4
 
| 4
| PlayerEyeColorId
+
| Inkling PlayerEyeColorId
 
| [https://pm1.narvii.com/6517/c094628ac4f3c695a65c14ca30099219a5c35f61_hq.jpg 0 to 13, each value represents a color]
 
| [https://pm1.narvii.com/6517/c094628ac4f3c695a65c14ca30099219a5c35f61_hq.jpg 0 to 13, each value represents a color]
 
|-
 
|-
Line 263: Line 304:
 
| 0x10
 
| 0x10
 
| 0x8
 
| 0x8
| (Time. Last used time?)
+
| Last Time Equipped
 
| UNIX Epoch time
 
| UNIX Epoch time
 
|-
 
|-
 
| 0x18
 
| 0x18
 
| 0x4
 
| 0x4
| unk
+
| Freshness Level
|  
+
| 3
 
|-
 
|-
 
| 0x1C
 
| 0x1C
 
| 0x4
 
| 0x4
| unk
+
| Flag Color
 
|  
 
|  
 
|-
 
|-
Line 305: Line 346:
 
| 0x13038
 
| 0x13038
 
| 4
 
| 4
| (EquippedShoeId)
+
| Equipped Shoe Id
 
| See Mush/GearInfo_Shoes.byml
 
| See Mush/GearInfo_Shoes.byml
 
|-
 
|-
Line 311: Line 352:
 
| 0x1303C
 
| 0x1303C
 
| 4
 
| 4
| (EquippedClothesId)
+
| Equipped Clothes Id
 
| See Mush/GearInfo_Clothes.byml
 
| See Mush/GearInfo_Clothes.byml
 
|-
 
|-
Line 317: Line 358:
 
| 0x13040
 
| 0x13040
 
| 4
 
| 4
| (EquippedHeadGearId)
+
| Equipped HeadGear Id
 
| See Mush/GearInfo_Head.byml
 
| See Mush/GearInfo_Head.byml
 
|-
 
|-
Line 371: Line 412:
 
| 0x4
 
| 0x4
 
| 4
 
| 4
| (Total # of unlocked abilities - including main slot)
+
| Gear Level (Total # of unlocked abilities - including main slot)
 
| 1, 2, 3, or 4
 
| 1, 2, 3, or 4
 
|-
 
|-
 
| 0x8
 
| 0x8
 
| 4
 
| 4
| (Total # of ability slots - including main slot)
+
| Gear Slots Unlocked (Total # of ability slots - including main slot)
 
| 1, 2, 3, or 4
 
| 1, 2, 3, or 4
 
|-
 
|-
 
| 0xC
 
| 0xC
 
| 4
 
| 4
| (Main slot ability id)
+
| Main slot ability id
| To be filled in
+
| See SkillInfo.byml
 
|-
 
|-
 
| 0x10
 
| 0x10
 
| 4
 
| 4
| (Sub slot#1 ability id)
+
| Sub slot#1 ability id
| To be filled in
+
| See SkillInfo.byml
 
|-
 
|-
 
| 0x14
 
| 0x14
 
| 4
 
| 4
| (Sub slot#1 ability id)
+
| Sub slot#2 ability id
| To be filled in
+
| See SkillInfo.byml
 
|-
 
|-
 
| 0x18
 
| 0x18
 
| 4
 
| 4
| (Sub slot#1 ability id)
+
| Sub slot#3 ability id
| To be filled in
+
| See SkillInfo.byml
 
|-
 
|-
 
| 0x1C
 
| 0x1C
 
| 4
 
| 4
| unk
+
| Current Experience
|  
+
| Value between 0 and [maxexp -1]
 
|-
 
|-
 
| 0x20
 
| 0x20
 
| 0x8
 
| 0x8
| Time (Last used time?)
+
| Last Time Equipped
 
| UNIX Epoch time
 
| UNIX Epoch time
 
|-
 
|-
Line 420: Line 461:
 
|}
 
|}
  
SkillChip (Ability Chuncks) Inventory part:
+
SkillChip (Ability Chunks) Inventory part:
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
|-
 
|-
Line 433: Line 474:
 
| 0x8
 
| 0x8
 
| 0x200
 
| 0x200
| (SkillChip (a.k.a. Ability Chuncks) Inventory)
+
| (SkillChip (a.k.a. Ability Chunks) Inventory)
 
|}
 
|}
  
Line 446: Line 487:
 
| 0x0
 
| 0x0
 
| 4
 
| 4
| (SkillId?)
+
| Skill ID
| To be filled in
+
| See SkillInfo.byml
 
|-
 
|-
 
| 0x4
 
| 0x4
 
| 4
 
| 4
| (Skill chip amount)
+
| Skill chip amount
 
| 696
 
| 696
 
|}
 
|}
Line 481: Line 522:
 
| 0x0
 
| 0x0
 
| 4
 
| 4
| (Ticket Id) (for what kind of food or drink)
+
| Type ID
| To be filled in
+
| 0 = +50% Exp, 1 = +100% Exp, 2 = +50% Money, 3 = +100% Money, 4 = Skill Drink, 5 = +150% Exp, 6 = +150% Money
 
|-
 
|-
 
| 0x4
 
| 0x4
 
| 4
 
| 4
| (Boosted skill id for drinks) (-1 for food tickets)
+
| Boosted skill id for drinks (-1 for food tickets)
| To be filled in
+
| See SkillInfo.byml
 
|-
 
|-
 
| 0xC
 
| 0xC
 
| 4
 
| 4
| (Ticket amount)
+
| Ticket amount
 
| 69
 
| 69
 
|}
 
|}
Line 525: Line 566:
 
| 0x25544
 
| 0x25544
 
| 0x25554
 
| 0x25554
| 0x4
+
| 0x1
 
| CtrlOption (Handheld motion ctrl flag)
 
| CtrlOption (Handheld motion ctrl flag)
 
| 1 for enabled
 
| 1 for enabled
 
|-
 
|-
| 0x25548
+
| 0x25545
| 0x25558
+
| 0x25555
| 0x40
+
| 0x1
 +
| CtrlOption (Handheld motion invert Y-axis)
 +
| 1 for enabled
 +
|-
 +
| 0x25546
 +
| 0x25556
 +
| 0x1
 +
| CtrlOption (Handheld motion invert X-axis)
 +
| 1 for enabled
 +
|-
 +
| 0x25547
 +
| 0x25557
 +
| 0x41
 
| CtrlOption (unk)
 
| CtrlOption (unk)
 
| A buncha 0s?
 
| A buncha 0s?
Line 549: Line 602:
 
| 0x25590
 
| 0x25590
 
| 0x255A0
 
| 0x255A0
| 0x4
+
| 0x1
 
| CtrlOption (TV/Tabletop motion ctrl flag)
 
| CtrlOption (TV/Tabletop motion ctrl flag)
 
| 1 for enabled
 
| 1 for enabled
 
|-
 
|-
| 0x25594
+
| 0x25591
| 0x255A4
+
| 0x255A1
| 0x40
+
| 0x1
 +
| CtrlOption (TV/Tabletop invert Y-axis)
 +
| 1 for enabled
 +
|-
 +
| 0x25592
 +
| 0x255A2
 +
| 0x1
 +
| CtrlOption (TV/Tabletop invert X-axis)
 +
| 1 for enabled
 +
|-
 +
| 0x25593
 +
| 0x255A3
 +
| 0x41
 
| CtrlOption (unk)
 
| CtrlOption (unk)
 
| A buncha 0s?
 
| A buncha 0s?
Line 568: Line 633:
 
| 0x255F8
 
| 0x255F8
 
| 0x8
 
| 0x8
| OnlinePlayPenalty Time (penalty start time?)
+
| OnlinePlayPenalty Time (penalty end time)
 
| UNIX Epoch time
 
| UNIX Epoch time
 
|-
 
|-
Line 603: Line 668:
 
| 0x25890
 
| 0x25890
 
| 0x258A0
 
| 0x258A0
| 0x4458
+
| 0x54
 
| unk (Haven't looked into yet)
 
| unk (Haven't looked into yet)
 
|  
 
|  
 
|-
 
|-
| 0x29CE8
+
| 0x258E4
| 0x29CF8
+
| 0x258F4
| 0x1528
+
| 0x1
| unk (Padding?)
+
| Color Lock
| A buncha 0?
+
| 1 for enabled
|}
+
|-
----
+
| 0x258E5
'''SaveDataVss(0x2B220):'''
+
| 0x258F5
{| class="wikitable" border="1"
+
| 0x1
 +
| Coconut Post Display
 +
| 1 for enabled
 
|-
 
|-
! Offset
+
| 0x258E6
! Absolute Offset
+
| 0x258F6
! Size
+
| 0x2BEA
! Description
+
| unk (Haven't looked into yet)
! Value
 
 
|-
 
|-
| 0x0
+
| 0x284D0
| 0x2B220
+
| 0x284E0
 
| 4
 
| 4
| PlayerRank (Not rank, but level. They call it rank in the code.)
+
| Octoling PlayerModelType (Gender, basically)
| Looks like level 1 is `0` and level 2 is `1`, etc)
+
| |2: Female Veemo| 3: Male Veemo
 
|-
 
|-
| 0x4
+
| 0x284D4
| 0x2B224
+
| 0x284E4
 
| 4
 
| 4
| Experience
+
| Octoling PlayerHairId
| To be filled in
+
| See Mush/HairInfo.byml
 
|-
 
|-
| 0x8
+
| 0x284D8
| 0x2B228
+
| 0x284E8
 
| 4
 
| 4
| StarRank
+
| Octoling PlayerBottomId
| To be filled in
+
| See Mush/BottomInfo.byml
 
|-
 
|-
| 0x14
+
| 0x284DC
| 0x2B234
+
| 0x284EC
 
| 4
 
| 4
| (TicketBoostRemaining)
+
| Octoling PlayerSkinColorId
| 20
+
| 0 to 6, 0 being lightest and 6 darkest
 
|-
 
|-
| 0x8768
+
| 0x284E0
| 0x33988
+
| 0x284F0
 
| 4
 
| 4
| (X Power Rainmaker)
+
| Octoling PlayerEyeColorId
| 2770.2 (Float)
+
| [https://pm1.narvii.com/6517/c094628ac4f3c695a65c14ca30099219a5c35f61_hq.jpg 0 to 13, each value represents a color]
 
|-
 
|-
| 0x87B0
+
| 0x284E4
| 0x339D0
+
| 0x284F4
 
| 4
 
| 4
| (X Power Splatzone)
+
| Is Octoling
| 2809.7 (Float)
+
| 1 if yes
 +
|-
 +
| 0x284E8
 +
| 0x284F8
 +
| 0x2D2C
 +
| unk (Haven't looked into yet)
 +
|}
 +
 
 +
=== SaveDataVss ===
 +
{| class="wikitable"
 +
! Offset !! Absolute Offset !! Size !! Description !! Value
 +
|-
 +
| 0x0 || 0x2B220 || 4 || PlayerRank (Not rank, but level. They call it rank in the code.) || Looks like level 1 is 0 and level 2 is 1, etc
 +
|-
 +
| 0x4 || 0x2B224 || 4 || Experience || To be filled in
 +
|-
 +
| 0x8 || 0x2B228 || 4 || StarRank || To be filled in
 +
|-
 +
| 0x14 || 0x2B234 || 4 || (TicketBoostRemaining) || 20
 +
|-
 +
| 0x18 || 0x2B238 || 4 || Rainmaker rank || 0 = C-, 1 = C, 2 = C+, etc
 +
|-
 +
| 0x24 || 0x2B244 || 4 || Splat Zones rank || See above
 +
|-
 +
| 0x30 || 0x2B250 || 4 || Tower Control rank || See above
 +
|-
 +
| 0x3C || 0x2B25C || 4 || Clam Blitz rank || See above
 +
|-
 +
| 0x60 || 0x2B280 || 4 || Turf War MMR/Power || 1969.6 (Float)
 +
|-
 +
| 0x64 || 0x2B284 || 4 || Turf War Glicko2 rating deviation || 69.6 (Float)
 +
|-
 +
| 0x68 || 0x2B288 || 4 || Turf War Glicko2 volatility || 0.06 (Float)
 +
|-
 +
| 0x6C || 0x2B28C || 4 || Turf War calculation state (?) || 1
 +
|-
 +
| 0x70 || 0x2B290 || 4 || Rainmaker MMR/Power || 1969.6 (Float)
 +
|-
 +
| 0x74 || 0x2B294 || 4 || Rainmaker Glicko2 rating deviation || 69.6 (Float)
 +
|-
 +
| 0x78 || 0x2B298 || 4 || Rainmaker Glicko2 volatility || 0.06 (Float)
 +
|-
 +
| 0x7C || 0x2B29C || 4 || Rainmaker calculation state (?) || 1
 +
|-
 +
| 0x80 || 0x2B2A0 || 4 || Splat Zones MMR/Power || 1969.6 (Float)
 +
|-
 +
| 0x84 || 0x2B2A4 || 4 || Splat Zones Glicko2 rating deviation || 69.6 (Float)
 +
|-
 +
| 0x88 || 0x2B2A8 || 4 || Splat Zones Glicko2 volatility || 0.06 (Float)
 +
|-
 +
| 0x8C || 0x2B2AC || 4 || Splat Zones calculation state (?) || 1
 +
|-
 +
| 0x90 || 0x2B2B0 || 4 || Tower Control MMR/Power || 1969.6 (Float)
 +
|-
 +
| 0x94 || 0x2B2B4 || 4 || Tower Control Glicko2 rating deviation || 69.6 (Float)
 +
|-
 +
| 0x98 || 0x2B2B8 || 4 || Tower Control Glicko2 volatility || 0.06 (Float)
 +
|-
 +
| 0x9C || 0x2B2BC || 4 || Tower Control calculation state (?) || 1
 +
|-
 +
| 0xA0 || 0x2B2C0 || 4 || Clam Blitz MMR/Power || 1969.6 (Float)
 +
|-
 +
| 0xA4 || 0x2B2C4 || 4 || Clam Blitz Glicko2 rating deviation || 69.6 (Float)
 +
|-
 +
| 0xA8 || 0x2B2C8 || 4 || Clam Blitz Glicko2 volatility || 0.06 (Float)
 +
|-
 +
| 0xAC || 0x2B2CC || 4 || Tower Control calculation state (?) || 1
 +
|-
 +
| 0x260 || 0x2B480 || 0x2800 || Pair League Battle results || TagScore Array (32)
 +
|-
 +
| 0x2A60 || 0x2DC80 || 0x2800 || Team League Battle results || TagScore Array (32)
 +
|-
 +
| 0x5260 || 0x30480 || 0x2C0 || ??? || LeagueScore Array (8)
 +
|-
 +
| 0x5520 || 0x30740 || 0x2C0 || ??? || LeagueScore Array (8)
 +
|-
 +
| 0x57E0 || 0x30A00 || 0x2C0 || ??? || LeagueScore Array (8)
 +
|-
 +
| 0x5AA0 || 0x30CC0 || 0x2C0 || ??? || LeagueScore Array (8)
 +
|-
 +
| 0x5EB0 || 0x310D0 || 0x28A0 || Plaza NPCs (?) || PlayerInfo Array (25)
 +
|-
 +
| 0x8768 || 0x33988 || 4 || Rainmaker X Power || 2270.2 (Float)
 +
|-
 +
| 0x87B0 || 0x339D0 || 4 || Splat Zones X Power || 2309.7 (Float)
 +
|-
 +
| 0x87F8 || 0x33A18 || 4 || Tower Control X Power || 2301.4 (Float)
 +
|-
 +
| 0x8840 || 0x33A60 || 4 || Clam Blitz X Power || 2297.1 (Float)
 +
|}
 +
 
 +
Structure of Entry (TagPlayers):
 +
{| class="wikitable"
 +
! Offset !! Size !! Description !! Value
 +
|-
 +
| 0x0 || 8 || Player 1 NEX Unique ID || Needs investigation
 +
|-
 +
| 0x8 || 0x28 || Player 1 system info || PlayerSystemInfo
 +
|-
 +
| 0x30 || 8 || Player 2 NEX Unique ID || Needs investigation
 +
|-
 +
| 0x38 || 0x28 || Player 2 system info || PlayerSystemInfo
 +
|-
 +
| 0x60 || 8 || Player 3 NEX Unique ID || Needs investigation
 +
|-
 +
| 0x68 || 0x28 || Player 3 system info || PlayerSystemInfo
 +
|-
 +
| 0x90 || 8 || Player 4 NEX Unique ID || Needs investigation
 +
|-
 +
| 0x98 || 0x28 || Player 4 system info || PlayerSystemInfo
 +
|}
 +
 
 +
Structure of Entry (PlayerSystemInfo):
 +
{| class="wikitable"
 +
! Offset !! Size !! Description !! Value
 +
|-
 +
| 0x8 || 0x14 || In-game name || UTF-16LE
 +
|}
 +
 
 +
Structure of Entry (PlayerInfo):
 +
{| class="wikitable"
 +
! Offset !! Size !! Description !! Value
 +
|-
 +
| 0x16 || 0x14 || In-game name || UTF-16LE
 +
|-
 +
| 0x40 || 4 || PlayerHairId || See Mush/HairInfo.byml
 +
|-
 +
| 0x44 || 4 || PlayerSkinColorId || 0 to 6, 0 being lightest and 6 darkest
 +
|-
 +
| 0x48 || 4 || PlayerEyeColorId || 0 to 13, each value represents a color
 +
|-
 +
| 0x64 || 0x20 || ??? || Gear
 +
|-
 +
| 0x84 || 0x20 || ??? || Gear
 +
|-
 +
| 0xA4 || 0x20 || ??? || Gear
 +
|-
 +
| 0xC4 || 4 || Tank || See Mush/TankInfo.byml
 +
|-
 +
| 0xC8 || 4 || PlayerBottomId || See Mush/BottomInfo.byml
 +
|-
 +
| 0xDC || 4 || Level || Looks like level 1 is 0 and level 2 is 1, etc
 
|-
 
|-
| 0x87F8
+
| 0xE0 || 4 || Stars ||
| 0x33A18
+
|-
| 4
+
| 0xE4 || 4 || Rainmaker rank || 0 = C-, 1 = C, 2 = C+, etc
| (X Power Tower Control)
+
|-
| 2801.4 (Float)
+
| 0xE8 || 4 || Splat Zones rank || See above
 +
|-
 +
| 0xEC || 4 || Tower Control rank || See above
 
|-
 
|-
| 0x8840
+
| 0xF0 || 4 || Clam Blitz rank || See above
| 0x33A60
 
| 4
 
| (X Power Clam Blitz)
 
| 2797.1 (Float)
 
 
|}
 
|}
----
+
 
'''SaveDataLocal(0x37970):'''
+
=== SaveDataLocal ===
----
+
=== SaveDataMsn ===
'''SaveDataMsn(0x3AD58):'''
 
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
|-
 
|-
Line 692: Line 896:
 
| 0x3CBC8
 
| 0x3CBC8
 
| 4
 
| 4
| (MechanicalFish)
+
| (Sardinium)
 
| 69
 
| 69
 
|}
 
|}
----
+
=== SaveDataShop ===
'''SaveDataShop(0x40BD0):'''
+
=== SaveDataCoop ===
----
+
=== SaveDataFest ===
'''SaveDataCoop(0x438D0?):'''
+
=== SaveDataStats ===
----
+
=== SaveDataMsnOcta ===
More to be added here...
+
 
----
+
=== Footer ===
'''Save Footer:'''<br />
 
 
Used for save crypto (as of ver 4)
 
Used for save crypto (as of ver 4)
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
Line 722: Line 925:
 
| AES-CMAC
 
| AES-CMAC
 
|}
 
|}
 +
 +
[[Category:Splatoon 2]]

Latest revision as of 20:11, 26 June 2019

Splatoon 2 saves its data in save.dat.

Tools

  • SplatHeX: a full fledged save editor for Windows.
  • Splatsave: an open source program that decrypts and re-encrypts save.dat.

Revisions

Since release, there has been 7 revisions to this file:

Version Added On Save Size Description
0 1.0.0 0x483B0 Original shipped version, saves in plain text.
1 1.1.0 0x483E0 Introduced encryption. Added in day 1 patch.
2 2.3.0 0x86840 Added Stats Data (SaveDataStats)
3 3.0.0 0x88D90 Added Octo Expansion Data (SaveDataMsnOcta). Introduced new key look-up table.
4 4.0.0 0x88D90 Introduced save body shuffle and new key look-up table. Content is the same as Ver 3.
5 4.2.0 0x88D90 Same look-up table as Ver 4, but removed shuffling. Content is still the same as Ver 3.
6 4.3.0 0x88D90 Internal version. Does not appear in actual saved files. Mostly likely it's the same encoding as before but converted added abilities.
7 4.3.0 0x88D90 Introduced new look-up table and now shuffles twice, before and after encryption. Content is presumably still the same as Ver 3.
8 4.3.1 0x88D90 Introduced additional encryption for each shuffle blocks. Shuffling now uses bit reversed/flipped CRC32 checksum as seed. Content is still the same as Ver 3.

Encoding

The encoding process takes place in Cmn::SaveDataFactory::encode. The decoding process takes place in Cmn::SaveDataFactory::decode.

The encryption used on the save body is AES-128 CBC. The key is generated using a sead::Random object initialized from a 16 byte seed stored in the save footer, in which also stores the initialization vector and the CMAC for authentication. The CMAC key is generated from the same sead::Random object after the encryption key. The key is generated by Cmn::SaveDataFactory::Impl::IEncodedAllAccessor::createKey.

For version 4, a shuffling process for save body was added to take place after the save body gets encrypted. This shuffling is not present in version five, but is done twice in version 7 and onward, with a shuffle before the body encryption as well. In save version 8, the shuffling doubles as an added layer of encryption. For determining the block sizes to shuffle, the game constructs a new sead::Random object. In version 4 and 7, this sead::Random object is initialized simply with the CRC32 checksum from the save header; in version 8, rather, the checksum is bit- reversed before being used for the after-encryption shuffle, and bit-reversed, plus bit-flipped for the before-encryption shuffle. The game then uses the sead::Random object to generate block sizes larger than 1/16 of body size and smaller than 1/8 of body size (or total remaining size, in case the remaining size is smaller than 1/8 of body size). When the remaining size is smaller than 1/16 of body size, it is added as the size of the last block. The body size for version 8, despite following the same general principal, goes an extra step to make sure that all the generated block sizes are multiple of 0x10, for the added "shuffle encryption" purpose. The game uses sead::PtrArrayImpl::shuffle with the same previous sead::Random object to complete the shuffle. While in version 4 and 7, the shuffling is done by simply copying over the block buffers, the version 8 save creates more sead::Random objects for each block, initialized with the respective block size. The game uses these sead::Random objects to generate the IV used for block encryption, and then chains the object into Cmn::SaveDataFactory::Impl::IEncodedAllAccessor::createKey, the same key generation function used in "whole-body" encryption, to create the encryption key. Lastly, all the blocks are encrypted into their shuffled position with the existing AES-128 CBC algorithm.

File Structure

Header

Offset Size Description Value
0x0 4 (Save Version) 0, 1, 2, 3 or 4, etc
0x4 4 (Dev Save Version?)
0x8 4 crc32 of save body (everything between header and footer) DEADBEAF
0xC 4 (Padding) 0

Body Structure

Offset Absolute Offset Size Description
0x0 0x10 0x2B210 SaveDataCmn
0x2B210 0x2B220 0xC750 SaveDataVss
0x37960 0x37970 0x33E8 SaveDataLocal
0x3AD48 0x3AD58 0x5E78 SaveDataMsn
0x40BC0 0x40BD0 0x2D00 SaveDataShop
0x438C0 0x438D0 0x49C0 SaveDataCoop
0x48280 0x48290 0x11C SaveDataFest
0x4839C 0x483AC 0x3E460 SaveDataStats
0x867FC 0x8680C 0x2544 SaveDataMsnOcta

SaveDataCmn

Offset Absolute Offset Size Description Value
0x0 0x10 4 Inkling PlayerModelType (Gender, basically) 0: Woomy | 1: Ngyes | 6: Story Octoling (Rev 0 and 1 only)
0x4 0x14 4 Inkling PlayerHairId See Mush/HairInfo.byml
0x8 0x18 4 Inkling PlayerBottomId See Mush/BottomInfo.byml
0xC 0x1C 4 Inkling PlayerSkinColorId 0 to 6, 0 being lightest and 6 darkest
0x10 0x20 4 Inkling PlayerEyeColorId 0 to 13, each value represents a color
0x14 0x24 4 Weapon (Equipped Main Weapon Id) See Mush/WeaponInfo_Main.byml
0x18 0x28 4 Weapon (Equipped Sub Weapon Id) See Mush/WeaponInfo_Sub.byml
0x1C 0x2C 4 Weapon (Equipped Special Weapon Id) See Mush/WeaponInfo_Special.byml
0x20 0x30 4 Weapon (Equipped Weapon Turf inked) 1234567
0x24 0x34 4 unk (padding?) 0?

Weapon Inventory part:

Offset Absolute Offset Size of Each Entry (HaveWeapon) Total Size Description
0x28 0x38 0x130 0x13000 (Weapon Inventory)

Structure of Entry (HaveWeapon)

Offset Size Description Value
0x0 4 Weapon (WeaponMain id) See Mush/WeaponInfo_Main.byml
0x4 4 Weapon (WeaponSub id) See Mush/WeaponInfo_Sub.byml
0x8 4 Weapon (WeaponSp id) See Mush/WeaponInfo_Special.byml
0xC 4 Weapon (Turf inked) 1234567
0x10 0x8 Last Time Equipped UNIX Epoch time
0x18 0x4 Freshness Level 3
0x1C 0x4 Flag Color
0x20 0x108 unk (Some weird block, likely represents various flags) A buncha 0x55555555, bit flipped to 0xAAAAAAAA at some places
0x128 4 (New or not) 0 = new, 1 = not new
0x12C 4 unk (padding?) 0?

SaveDataCmn (Cont'd):

Offset Absolute Offset Size Description Value
0x13028 0x13038 4 Equipped Shoe Id See Mush/GearInfo_Shoes.byml
0x1302C 0x1303C 4 Equipped Clothes Id See Mush/GearInfo_Clothes.byml
0x13030 0x13040 4 Equipped HeadGear Id See Mush/GearInfo_Head.byml
0x13034 0x13044 4 unk (padding?) 0?

Gear Inventory part:

Offset Absolute Offset Size of Each Entry (HaveGear) Total Size Description
0x13038 0x13048 0x30 0x6000 (Shoe Inventory)
0x19038 0x19048 0x30 0x6000 (Clothes Inventory)
0x1F038 0x1F048 0x30 0x6000 (HeadGearInventory)

Structure of Entry (HaveGear):

Offset Size Description Value
0x0 4 (Gear id) See GearInfo_XXXX.byml
0x4 4 Gear Level (Total # of unlocked abilities - including main slot) 1, 2, 3, or 4
0x8 4 Gear Slots Unlocked (Total # of ability slots - including main slot) 1, 2, 3, or 4
0xC 4 Main slot ability id See SkillInfo.byml
0x10 4 Sub slot#1 ability id See SkillInfo.byml
0x14 4 Sub slot#2 ability id See SkillInfo.byml
0x18 4 Sub slot#3 ability id See SkillInfo.byml
0x1C 4 Current Experience Value between 0 and [maxexp -1]
0x20 0x8 Last Time Equipped UNIX Epoch time
0x28 4 (New or not) 0 = new, 1 = not new
0x2C 4 unk (padding?) 0?

SkillChip (Ability Chunks) Inventory part:

Offset Absolute Offset Size of Each Entry (HaveSkillChip) Total Size Description
0x25038 0x25048 0x8 0x200 (SkillChip (a.k.a. Ability Chunks) Inventory)

Structure of Entry (HaveSkillChip):

Offset Size Description Value
0x0 4 Skill ID See SkillInfo.byml
0x4 4 Skill chip amount 696

BoostTicket (Crusty Sean) Inventory part:

Offset Absolute Offset Size of Each Entry (HaveBoostTicket) Total Size Description
0x25238 0x25248 0xC 0x300 (BoostTicket Inventory)

Structure of Entry (HaveBoostTicket):

Offset Size Description Value
0x0 4 Type ID 0 = +50% Exp, 1 = +100% Exp, 2 = +50% Money, 3 = +100% Money, 4 = Skill Drink, 5 = +150% Exp, 6 = +150% Money
0x4 4 Boosted skill id for drinks (-1 for food tickets) See SkillInfo.byml
0xC 4 Ticket amount 69

SaveDataCmn (Cont'd):

Offset Absolute Offset Size Description Value
0x25538 0x25548 4 TutorialResult 0 for not seen tutorial, > 0 for seen tutorial?
0x2553C 0x2554C 0x4 CtrlOption (Handheld Stick) Float, 1.0 for +5, -1.0 for -5
0x25540 0x25550 0x4 CtrlOption (Handheld Motion) Float, 1.0 for +5, -1.0 for -5
0x25544 0x25554 0x1 CtrlOption (Handheld motion ctrl flag) 1 for enabled
0x25545 0x25555 0x1 CtrlOption (Handheld motion invert Y-axis) 1 for enabled
0x25546 0x25556 0x1 CtrlOption (Handheld motion invert X-axis) 1 for enabled
0x25547 0x25557 0x41 CtrlOption (unk) A buncha 0s?
0x25588 0x25598 0x4 CtrlOption (TV/Tabletop Stick) Float, 1.0 for +5, -1.0 for -5
0x2558C 0x2559C 0x4 CtrlOption (TV/Tabletop Motion) Float, 1.0 for +5, -1.0 for -5
0x25590 0x255A0 0x1 CtrlOption (TV/Tabletop motion ctrl flag) 1 for enabled
0x25591 0x255A1 0x1 CtrlOption (TV/Tabletop invert Y-axis) 1 for enabled
0x25592 0x255A2 0x1 CtrlOption (TV/Tabletop invert X-axis) 1 for enabled
0x25593 0x255A3 0x41 CtrlOption (unk) A buncha 0s?
0x255D4 0x255E4 0x10 OnlinePlayPenalty (4 values) Haven't looked into yet
0x255E8 0x255F8 0x8 OnlinePlayPenalty Time (penalty end time) UNIX Epoch time
0x255F0 0x25600 0x188 unk (Haven't looked into yet)
0x25778 0x25788 4 HaveMoney 9696969
0x2577C 0x2578C 4 HaveTurbanShell (Sea snails) 969
0x25780 0x25790 0x108 unk (Haven't looked into yet)
0x25888 0x25898 8 Time (time of save?) UNIX Epoch time
0x25890 0x258A0 0x54 unk (Haven't looked into yet)
0x258E4 0x258F4 0x1 Color Lock 1 for enabled
0x258E5 0x258F5 0x1 Coconut Post Display 1 for enabled
0x258E6 0x258F6 0x2BEA unk (Haven't looked into yet)
0x284D0 0x284E0 4 Octoling PlayerModelType (Gender, basically) 2: Female Veemo| 3: Male Veemo
0x284D4 0x284E4 4 Octoling PlayerHairId See Mush/HairInfo.byml
0x284D8 0x284E8 4 Octoling PlayerBottomId See Mush/BottomInfo.byml
0x284DC 0x284EC 4 Octoling PlayerSkinColorId 0 to 6, 0 being lightest and 6 darkest
0x284E0 0x284F0 4 Octoling PlayerEyeColorId 0 to 13, each value represents a color
0x284E4 0x284F4 4 Is Octoling 1 if yes
0x284E8 0x284F8 0x2D2C unk (Haven't looked into yet)

SaveDataVss

Offset Absolute Offset Size Description Value
0x0 0x2B220 4 PlayerRank (Not rank, but level. They call it rank in the code.) Looks like level 1 is 0 and level 2 is 1, etc
0x4 0x2B224 4 Experience To be filled in
0x8 0x2B228 4 StarRank To be filled in
0x14 0x2B234 4 (TicketBoostRemaining) 20
0x18 0x2B238 4 Rainmaker rank 0 = C-, 1 = C, 2 = C+, etc
0x24 0x2B244 4 Splat Zones rank See above
0x30 0x2B250 4 Tower Control rank See above
0x3C 0x2B25C 4 Clam Blitz rank See above
0x60 0x2B280 4 Turf War MMR/Power 1969.6 (Float)
0x64 0x2B284 4 Turf War Glicko2 rating deviation 69.6 (Float)
0x68 0x2B288 4 Turf War Glicko2 volatility 0.06 (Float)
0x6C 0x2B28C 4 Turf War calculation state (?) 1
0x70 0x2B290 4 Rainmaker MMR/Power 1969.6 (Float)
0x74 0x2B294 4 Rainmaker Glicko2 rating deviation 69.6 (Float)
0x78 0x2B298 4 Rainmaker Glicko2 volatility 0.06 (Float)
0x7C 0x2B29C 4 Rainmaker calculation state (?) 1
0x80 0x2B2A0 4 Splat Zones MMR/Power 1969.6 (Float)
0x84 0x2B2A4 4 Splat Zones Glicko2 rating deviation 69.6 (Float)
0x88 0x2B2A8 4 Splat Zones Glicko2 volatility 0.06 (Float)
0x8C 0x2B2AC 4 Splat Zones calculation state (?) 1
0x90 0x2B2B0 4 Tower Control MMR/Power 1969.6 (Float)
0x94 0x2B2B4 4 Tower Control Glicko2 rating deviation 69.6 (Float)
0x98 0x2B2B8 4 Tower Control Glicko2 volatility 0.06 (Float)
0x9C 0x2B2BC 4 Tower Control calculation state (?) 1
0xA0 0x2B2C0 4 Clam Blitz MMR/Power 1969.6 (Float)
0xA4 0x2B2C4 4 Clam Blitz Glicko2 rating deviation 69.6 (Float)
0xA8 0x2B2C8 4 Clam Blitz Glicko2 volatility 0.06 (Float)
0xAC 0x2B2CC 4 Tower Control calculation state (?) 1
0x260 0x2B480 0x2800 Pair League Battle results TagScore Array (32)
0x2A60 0x2DC80 0x2800 Team League Battle results TagScore Array (32)
0x5260 0x30480 0x2C0 ??? LeagueScore Array (8)
0x5520 0x30740 0x2C0 ??? LeagueScore Array (8)
0x57E0 0x30A00 0x2C0 ??? LeagueScore Array (8)
0x5AA0 0x30CC0 0x2C0 ??? LeagueScore Array (8)
0x5EB0 0x310D0 0x28A0 Plaza NPCs (?) PlayerInfo Array (25)
0x8768 0x33988 4 Rainmaker X Power 2270.2 (Float)
0x87B0 0x339D0 4 Splat Zones X Power 2309.7 (Float)
0x87F8 0x33A18 4 Tower Control X Power 2301.4 (Float)
0x8840 0x33A60 4 Clam Blitz X Power 2297.1 (Float)

Structure of Entry (TagPlayers):

Offset Size Description Value
0x0 8 Player 1 NEX Unique ID Needs investigation
0x8 0x28 Player 1 system info PlayerSystemInfo
0x30 8 Player 2 NEX Unique ID Needs investigation
0x38 0x28 Player 2 system info PlayerSystemInfo
0x60 8 Player 3 NEX Unique ID Needs investigation
0x68 0x28 Player 3 system info PlayerSystemInfo
0x90 8 Player 4 NEX Unique ID Needs investigation
0x98 0x28 Player 4 system info PlayerSystemInfo

Structure of Entry (PlayerSystemInfo):

Offset Size Description Value
0x8 0x14 In-game name UTF-16LE

Structure of Entry (PlayerInfo):

Offset Size Description Value
0x16 0x14 In-game name UTF-16LE
0x40 4 PlayerHairId See Mush/HairInfo.byml
0x44 4 PlayerSkinColorId 0 to 6, 0 being lightest and 6 darkest
0x48 4 PlayerEyeColorId 0 to 13, each value represents a color
0x64 0x20 ??? Gear
0x84 0x20 ??? Gear
0xA4 0x20 ??? Gear
0xC4 4 Tank See Mush/TankInfo.byml
0xC8 4 PlayerBottomId See Mush/BottomInfo.byml
0xDC 4 Level Looks like level 1 is 0 and level 2 is 1, etc
0xE0 4 Stars
0xE4 4 Rainmaker rank 0 = C-, 1 = C, 2 = C+, etc
0xE8 4 Splat Zones rank See above
0xEC 4 Tower Control rank See above
0xF0 4 Clam Blitz rank See above

SaveDataLocal

SaveDataMsn

Offset Absolute Offset Size Description Value
0x1E6C 0x3CBC4 4 (HeroModePowerOrb) 6969
0x1E70 0x3CBC8 4 (Sardinium) 69

SaveDataShop

SaveDataCoop

SaveDataFest

SaveDataStats

SaveDataMsnOcta

Footer

Used for save crypto (as of ver 4)

Offset Size Description
EOF-0x30 0x10 IV (initialization vector)
EOF-0x20 0x10 Key Seed
EOF-0x10 0x10 AES-CMAC