Skip to content

Instantly share code, notes, and snippets.

@wwylele
Created July 26, 2018 16:13
Show Gist options
  • Save wwylele/29a8caa6f5e5a7d88a00bedae90472ed to your computer and use it in GitHub Desktop.
Save wwylele/29a8caa6f5e5a7d88a00bedae90472ed to your computer and use it in GitHub Desktop.

Revisions

  1. wwylele created this gist Jul 26, 2018.
    194 changes: 194 additions & 0 deletions doc.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,194 @@
    # 3DS CEC (StreetPass) Documentation

    ## Overall Model

    The CEC system module consists of two main components: mailbox manager and StreetPass communicator. Both work on the system save 00010026, which stores all mailbox information and message data. A simplified diagram of CEC communication model is
    ```
    Application <=> Mailbox Manager <=> System Save (Mailbox) <=> StreetPass communicator <=> NWM/network <=> StreetPass communicator of another 3DS <=> ...
    ```

    Many CEC service (`cecd:u` and `cecd:s`) functions are interfaces exposed from the mailbox manager, which read and write the system save. The mailbox manager is essentially a thick layer over direct file IO, which performances data initialization and verification on top of raw system data read/write.

    The StreetPass communicator runs in the background, fetches data from the system save, sends and receives messages, and put new messages in the system save. It only exposed a few interfaces to control its activity, some of which are misc functions in `cecd:u` and `cecd:s`, and others are in `cecd:ndm` (which is ultimately exposed via NDM service `ndm:u`).

    ## Mailbox Manager Interfaces

    The mailbox manager exposes the following functions via `cecd:u` and `cecd:s`

    ```
    [0x0001](Result, DataSize) Open(TitleID, CecDataPathType, FileOption, PID);
    [0x0002](Result, DataSize) Read(DataSize, DataBuffer);
    [0x0003](Result, DataSize) ReadMessage(TitleID, BoxType, MessageIDSize, DataSize, MessageIDBuffer, DataBuffer);
    [0x0004](Result, DataSize) ReadMessageWithHmac(TitleID, BoxType, MessageIDSize, DataSize, MessageIDBuffer, HmacKeyBuffer, DataBuffer);
    [0x0005](Result ) Write(DataSize, DataBuffer);
    [0x0006](Result ) WriteMessage(TitleID, BoxType, MessageIDSize, DataSize, DataBuffer, MessageIDBuffer);
    [0x0007](Result ) WriteMessageWithHmac(TitleID, BoxType, MessageIDSize, DataSize, DataBuffer, HmacKeyBuffer, MessageIDBuffer);
    [0x0008](Result ) Delete(TitleID, CecDataPathType, BoxType, MessageIDSize, MessageIDBuffer);
    [0x0011](Result ) OpenAndWriteFile(DataSize, TitleID, CecDataPathType, FileOption, PID, DataBuffer);
    [0x0012](Result, DataSize) OpenAndReadFile(DataSize, TitleID, CecDataPathType, FileOption, PID, DataBuffer);
    ```

    Function signature convention:
    ```
    [Function Main ID](Reply list) Name(Param list)
    ```
    The reply list and param list are ordered according to their apperance in the IPC command buffer. Each param/reply takes one word, except for:
    - `PID` is the process ID descriptor and takes two words.
    - `xxxBuffer` is the mapped buffer descriptor and takes two words.

    Some notes of different parameter types are listed below
    - `TitleID` is a 32-bit ID as a mailbox identifier of a specific game. It is usually the same as game title ID
    - `CecDataPathType` is an enum refering to a specific file / folder in the system save. See TODO for the meaning of each value
    - `FileOption` are bitfields (starting from bit0):
    - bit1: read
    - bit2: write
    - bit3: create folder
    - bit4: skip some checks?
    - bit30: ?
    - `MessageIDSize` is always 8


    ## CEC System Save (00010026) Format

    ```
    Folder and files `CecDataPathType`
    [Root]
    ├── eventlog.dat
    └── CEC 10
    ├── MacFilter___
    ├── MBoxList____ 1
    ├── <8-digit ID> 11
    │ ├── MBoxInfo____ 2
    │ ├── MBoxData.001 101
    │ ├── MBoxData.010 110
    │ ├── MBoxData.050 150
    │ ├── MBoxData.<3-digit number> 100 + <number>
    │ ├── ...
    │ ├── InBox___ 12
    │ │ ├── BoxInfo_____ 3
    │ │ ├── _<12-char ID> 6
    │ │ └── ...
    │ └── OutBox__ 13
    │ ├── BoxInfo_____ 4
    │ ├── OBIndex_____ 5
    │ ├── _<12-char ID> 7
    │ └── ...
    ├── <8-digit ID>
    ...
    ```

    ### File `MBoxList____`

    |Offset|Length|Description|
    |-|-|-|
    |0x00|2|Magic 0x6868|
    |0x02|2|Padding|
    |0x04|4|Version? always 1|
    |0x08|4|Number of boxes|
    |0x0C|16 * 24|List of box name|
    Each box name is 16-char long. However, due to the fact that box name is usually a 8-digit ID, the rest of 8 chars are always null characters. Unused box names are filled with null characters.

    ### File `MBoxInfo____`

    |Offset|Length|Description|
    |-|-|-|
    |0x00|2|Magic 0x6363|
    |0x02|2|Padding|
    |0x04|4|Title ID (matches the box directory name)|
    |0x08|4|Private ID?|
    |0x0C|1|Flags?|
    |0x0D|1|Flags?|
    |0x0E|2|Padding|
    |0x10|32|HMAC Key
    |0x30|4|Zero?|
    |0x34|12|Timestamp when last accessed|
    |0x40|1|Flag?|
    |0x41|1|Flag?|
    |0x42|1|Flag?|
    |0x43|1|Flag?|
    |0x44|12|Timestamp when last received|
    |0x50|16|Zero?|

    Note:
    - `Private ID` seems to be a number chosen by application arbitrarily, possibly for verification. Magic numbers such as 0x00000000, 0x00000001, 0xFFFFFFFF, 0xAABBCCDD and other random numbers have been observed here.

    ### File `MBoxData.<3-digit number>`

    #### File `MBoxData.001`

    Icon

    #### File `MBoxData.010`

    This is the game title in null-terminated UTF-16 string.

    #### File `MBoxData.050`

    This is 8-byte file containing the title ID.

    ### File `BoxInfo_____`

    This file consists of a 0x20-byte header, and an array of 0x70-byte entry. Each entry is a copy of the message header. See the next section for the message header format. The box info header format is

    |Offset|Length|Description|
    |-|-|-|
    |0x00|2|Magic 0x6262|
    |0x02|2|Padding|
    |0x04|4|Size of this file|
    |0x08|4|Maximum box size|
    |0x0C|4|Current box size|
    |0x10|4|Maximum message count|
    |0x14|4|Current message count / the size of the following array|
    |0x18|4|Maximum batch size|
    |0x1C|4|Maximum message size|

    ### File `_<12-char ID>`

    Each such file is a message. The ID in the file name is the message ID encoded in base-64. A message file consists of a 0x70-byte header, several extra header, a message body, and a 0x20-byte HMAC(?).

    The header format is

    |Offset|Length|Description|
    |-|-|-|
    |0x00|2|Magic 0x6060|
    |0x02|2|Padding|
    |0x04|4|Message size|
    |0x08|4|Header + extra headers size|
    |0x0C|4|Body size|
    |0x10|4|Title ID|
    |0x14|4|Title ID 2?|
    |0x18|4|Batch ID|
    |0x1C|4|? ID|
    |0x20|8|Message ID|
    |0x28|4|Message version?|
    |0x2C|8|Message ID 2?|
    |0x34|1|Flags|
    |0x35|1|Send method|
    |0x36|1|Is unopen|
    |0x37|1|Is new|
    |0x38|8|Sender ID|
    |0x40|8|Sender ID 2?|
    |0x48|12|Timestamp when sent|
    |0x54|12|Timestamp when received|
    |0x60|12|Timestamp when created|
    |0x6C|1|Send count|
    |0x6D|1|Forward count|
    |0x6E|2|User data|

    Each extra header has a format of

    |Offset|Length|Description|
    |-|-|-|
    |0x00|4|Header type|
    |0x04|4|Data size|
    |0x08|Data size|Header data|

    The header type can be one of the follows:

    |Value|Description|
    |-|-|
    |1|?|
    |2|Icon|
    |3|Game name|
    |4|Info text|
    |5|Region?|