TO:R Data Map

pmf: obfuscated Ogg Theora movies

Source
docs/formats/pmf.md
SHA-256
219db7ea…849feddd

The game's full-motion movies, in movie/: the opening, the chapter movies, the trailer and one title movie per language. A .pmf is an ordinary Ogg file (one Theora video stream and one Ogg Skeleton stream, no audio) whose bytes are XORed with a repeating 60-byte key, RotXor's key 1, by file position. There is no header, no ZIP and no other wrapping. The game reads the file straight from disk, removes the XOR as it reads, and hands the bytes to libogg and libtheora. A movie's sound isn't in the .pmf: it is a separate sabf sound bank, played alongside and used as the movie's clock (Sound).

Based on All 17 files in movie/ (6,857,562,768 bytes in all; sizes in The files), the same paths and sizes as their FileTable.bin records; system/system_data.dat#6 and textseq/textseq_*.dat; the unpacked executable, SHA-256 7b0230a11498f40396c9048345e7cfe0ef442cb7ca8faffc6f197920d2669599. Outside specs: RFC 3533 (Ogg page layout and CRC), libtheora's codec.h (th_info, colour space and pixel format values), Ogg Skeleton 4, all checked 2026-10-11.
Checked 2026-10-11
Method Static analysis with Ghidra 12.1.4 through the decompiled code in local/exe/decomp/ (functions named by address below). Single-file C# exploration programs (session scratchpad, movie/): pmfscan.cs removes the XOR from every .pmf exactly as the game's read callback does (4096-byte reads from offset 0) and walks the result as Ogg pages, checking each page's capture pattern, version, CRC and sequence number, and reading the Theora and Skeleton headers; ftmovie.cs lists the file-table entries and dumps the tables that pick a movie's sound; textseq.cs reads the movie IDs out of the caption files. Not yet decoded or rebuilt by the toolchain. No in-game tests yet.

Layout

A .pmf is n bytes, with no header of its own.

Offset Size Type Name Meaning Confidence Source
0x0 n − (n mod 4) raw[n − (n mod 4)] ogg_words An Ogg file, as whole 4-byte words, each XORed with a key word: the word at offset 4w is XORed with word w mod 15 of the key. confirmed pmfscan.cs, all 17 files: every page's capture pattern, version and CRC are right after removing the XOR; 0x1402b0970 → 0x1401a2bf0.
n − (n mod 4) n mod 4 raw[n mod 4] ogg_tail The Ogg file's last 0 to 3 bytes, not XORed. confirmed pmfscan.cs: 14 of the 17 files have a tail of 1 to 3 bytes, and the CRC of every last page is right with the tail left as it is.

Key 1

The 15 key words are RotXor's key 1, the same 60 bytes at 0x140ba5300 that the .dat deobfuscation uses from start word 8. Here the start word is the word index in the file, so word w uses key word w mod 15, and the key repeats every 60 bytes of the file. Key word k is bytes 4k to 4k + 3 of the key, in order.

What the Ogg holds

Each file has two logical streams, both opened on the first pages and both ended with an end-of-stream page. These are data facts, confirmed by pmfscan.cs over all 17 files, unless marked.

Stream Pages What it is
Ogg Skeleton 4.0 4 The first page of the file. Packets fishead (version 4.0), one fisbone for the Theora stream, and an index of keyframes. Players may ignore it (Ogg Skeleton 4), and the game does.
Theora the rest The video.

The Theora identification header, the same in every file except where noted:

Field Value
Bitstream version 3.2.1
Frame size (frame_width × frame_height) 1920 × 1088
Picture size (pic_width × pic_height) 1920 × 1080, at pic_x 0, pic_y 4
Frame rate (fps_numerator / fps_denominator) 2997 / 100 (29.97 frames per second)
Pixel aspect ratio 1 : 1; 0 : 0 (unspecified) in the eight title_* files
Colour space 0 (unspecified)
Pixel format 0 (4:2:0)
keyframe_granule_shift 6
Encoder (comment header vendor string) Xiph.Org libtheora 1.2.0alpha 20100924 (Ptalarbvorm)

The files

Movies are file-table group 2000; the file ID picks the movie. Frames and length are from the Theora stream's last granule position. Sound is the sound bank row for the file ID. All values confirmed (ftmovie.cs, pmfscan.cs, system/system_data.dat#6), except the Plays column, which is likely (static analysis, Who plays a movie).

File ID Path Size n mod 4 Frames Length (s) Sound Plays
1 movie/Prologue.PMF 1,274,853,163 3 5,412 180.58 1012: sound/bin/ja/Movie/prologue.sab or en/ Caption sequence 1–2
2 movie/chapter1.pmf 93,062,497 1 3,530 117.78 100: sound/bin/OPN_movie/chapter1.sab Caption sequence 3–4
3 movie/chapter2a.pmf 132,179,871 3 2,232 74.47 1000: sound/bin/ja/Movie/chapter2A.sab or en/ Caption sequence 5–6
4 movie/chapter2b.pmf 209,409,231 3 2,130 71.07 1002: …/Movie/chapter2B.sab Caption sequence 7–8
5 movie/chapter3a.pmf 296,572,350 2 2,253 75.18 1004: …/Movie/chapter3A.sab Caption sequence 9–10
6 movie/chapter3b.pmf 263,473,704 0 2,282 76.14 1006: …/Movie/chapter3B.sab Caption sequence 11–12
7 movie/chapter3c.pmf 298,417,197 1 2,412 80.48 1008: …/Movie/chapter3C.sab Caption sequence 13–14
8 movie/chapter4.pmf 376,471,216 0 3,084 102.90 1010: …/Movie/chapter4.sab Caption sequence 15–16
9 movie/trailer.pmf 474,503,489 1 4,375 145.98 71: sound/bin/bgm/taco_BGM71.sab Caption sequence 17
10 movie/title_ja-jp.pmf 428,427,129 1 1,845 61.56 none Title, language 0
11 movie/title_en-us.pmf 428,696,523 3 1,845 61.56 none Title, language 1
12 movie/title_fr-fr.pmf 430,711,354 2 1,845 61.56 none Title, language 2
13 movie/title_de-de.pmf 430,493,277 1 1,845 61.56 none Title, language 3
14 movie/title_es-es.pmf 429,716,016 0 1,845 61.56 none Title, language 4
15 movie/title_zh-cn.pmf 430,259,687 3 1,845 61.56 none Title, language 5
16 movie/title_zh-tw.pmf 430,654,369 1 1,845 61.56 none Title, language 6
17 movie/title_ko-kr.pmf 429,661,695 3 1,845 61.56 none Title, language 7

How the game reads it

Static analysis of the unpacked executable, so every fact here is at most likely; the functions are in the names list.

Opening

Reading

What it checks

The decoding thread (0x1401e0ae0, started by 0x1401e29f0 with at most 75 decoded frames queued) reads the beginning-of-stream pages, then checks the Theora header. Nothing in the .pmf is checked by the game's own code before libogg sees it.

Sound

Who plays a movie

Making a replacement movie

What a mod has to produce, from the facts above. Not tested in game.

Leads