# HG changeset patch # User Thibaut Girka # Date 1314098297 -7200 # Node ID c5b074b0abbdb0004f354ce9f53239b98233aa11 # Parent 2a7b9d62c0c4fe1d1e75978088bf8d4aece4cbd9 Add T6RP documentation diff --git a/06/t6rp.xhtml b/06/t6rp.xhtml new file mode 100644 --- /dev/null +++ b/06/t6rp.xhtml @@ -0,0 +1,88 @@ + + + + + + T6RP format + + +

T6RP format

+

The T6RP format is the replay format used by EoSD.

+ +

It is composed of a header and an "encrypted" data section, which is itself composed of a header, per-stage structures and lists of keystates.

+ + +

Header

+
+typedef struct {
+    char magic[4]; // T6RP
+    uint16_t unknown; //TODO: always 0x0102
+                      // Seems to be a switch, if 0x0102 do something, else (whatever the value is), do something else
+    uint8_t player; // 0 = ReimuA, 1 = ReimuB, 2 = MarisaA, 3 = MarisaB
+    uint8_t rank; // 0 = Easy, 3 = Lunatic, 4 = Extra
+    uint32_t checksum; // (0x3f000318 + key + sum(c for c in decrypted_data)) % (2 ** 32)
+    uint16_t unknown2; //TODO: seems to be ignored by the game
+    uint8_t key;
+    char crypted_data[]; // crypted(a, i) = (a + key + 7*i) % 256
+                         // decrypted(c, i) = (c - key - 7*i) % 256
+} thrpy6_header_t;
+
+ +

Decrypting the data section.

+

As stated earlier, the data section is encrypted. Luckily, this encryption is quite simplistic, and encrypting/decrypting it is easy (see this snippet of python3 code):

+
+def decrypt(key, data):
+    return bytes((c - key - 7 * i) % 256 for i, c in enumerate(data))
+
+def encrypt(key, data):
+    return bytes((c + key + 7 * i) % 256 for i, c in enumerate(data))
+
+ +

Encrypted header

+
+typedef struct {
+    uint8_t unknown; //TODO: seems to be ignored by the game
+    char date[9]; // null-terminated string
+    char name[9]; // null-terminated string
+    uint16_t unknown2; //TODO: seems to be ignored by the game
+    uint32_t score; //TODO: Total score. seems to be ignored by the game
+    uint32_t unknown3; //TODO: seems to be ignored by the game
+    float slowdown_rate; // As a percentage, not a proper rate
+    uint8_t unknwon[4]; //TODO: seems to have no effect
+    uint32_t stage1_offset; // Offset of a thrpy6_stage_t from the start of the file (including the unencrypted header)
+    uint32_t stage2_offset;
+    uint32_t stage3_offset;
+    uint32_t stage4_offset;
+    uint32_t stage5_offset;
+    uint32_t stage6_offset;
+    uint32_t stage7_offset;
+} thrpy6_encrypted_header;
+
+ +

Stage entry

+
+typedef struct {
+    uint32_t score;
+    uint16_t random_seed;
+    uint16_t unknown1; //TODO: seems to be ignored by the game
+    uint8_t power;
+    int8_t lives;
+    int8_t bombs;
+    uint8_t difficulty; //TODO: WARNING: This has a huge effect on the game!
+    // It is also called rank (but we use the term "difficulty" because "rank" is the official name for Easy/Normal/Hard/Lunatic/Extra)
+    // See: http://en.touhouwiki.net/wiki/Embodiment_of_Scarlet_Devil/Gameplay#Rank
+    uint8_t unknown3[3]; //TODO: seems to be ignored by the game. Padding?
+    thrpy6_keystate_t keystates[];
+} thrpy6_stage_t;
+
+ +

Keystates

+
+struct {
+    uint32_t time; // Time of the event, most probably exprimed in frames
+    uint16_t keys; // 1 = shoot, 2 = bomb, 4 = focus, 8 = ?, 16 = up, 32 = down, 64 = left, 128 = right
+    uint16_t unknown; //TODO: seems to be ignored by the game
+} thrpy6_keystate_t;
+
+ + diff --git a/index.xhtml b/index.xhtml --- a/index.xhtml +++ b/index.xhtml @@ -12,6 +12,7 @@
  • PBG3 (dat archives)
  • STD (stages)
  • ECL (enemies and patterns)
  • +
  • T6RP (replays)
  • Opcodes