Skip to content
View in the app

A better way to browse. Learn more.

ResHax

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.
Help us keep the site running.
Zero Tolerance for Disrespect

Resident Evil Code Veronica X HD PS3 — MDL format reverse engineering / Noesis importer

Featured Replies

Hi,

I'm reverse engineering the .mdl model format used by Resident Evil Code: Veronica X HD.

I'm specifically looking for help from someone experienced with Noesis Python plugins / Capcom model formats to turn the information below into a working Noesis importer that actually displays the complete mesh correctly.

I already have a substantial amount of the format mapped, but the last part of the encoded face/UV streams is preventing me from producing a reliable complete importer.

I am not looking for an approximate mesh. The objective is a deterministic parser that reconstructs the complete geometry correctly.


1. MDL blocks

The files contain blocks identified by:

4D 44 4C BC

and sometimes:

4D 44 4C AD

The MDL blocks contain a table of entries.

Each entry is:

0x34 bytes

The important fields identified so far are:

entry + 0x08 = entry type
entry + 0x0C = relative target

The relative offsets are relative to the MDL block.


2. TYPE 0x06 / TYPE 0x16 mesh entries

TYPE 0x06 is confirmed to be a geometry/submesh descriptor.

For example, in en52a00.mdl:

MDL base       = 0xD50

entry           = base + 0x94
type            = 0x06
target          = base + 0x398

target + 0x10  -> vertex descriptor
target + 0x14  -> face-stream descriptor

Vertex descriptor:

VD                = 0x3E0 relative
vertex count      = 0x330 = 816
vertex data       = VD + 0x50

Therefore:

vertex data abs = 0x1180

The vertex data occupies:

0x1180 ... 0x7783

and the face stream begins at:

0x7784

The face-stream pointer itself is:

target + 0x14

with the actual stream beginning at:

face_pointer + 0x10

The same structure was observed in several independent models.


3. Confirmed examples

en92a00.mdl

MDL base          = 0x1884

TYPE 06 entry     = base + 0x114
target            = base + 0x654

vertex descriptor = 0x66C
vertex count      = 0x5EE = 1518

vertex data       = 0x1F40

face pointer      = relative 0xC470
face stream       = 0xDD04

Face stream starts:

13 25 00 04
7F 7F 7F FF
7F 7F 7F FF
08 34 40 09
42 02 18 C4 ...

en91a00.mdl

MDL base          = 0x1958

TYPE 06 entry     = base + 0x118
target            = base + 0x658

vertex descriptor = 0x698
vertex count      = 0x623 = 1571

vertex data       = 0x2040

face pointer      = relative 0xCB3C
face stream       = 0xE4A4

Face stream:

13 25 00 04
...
42 02 1E 80 ...

en05a00.mdl

Library sample:

file size = 58560 bytes
MDL base  = 0xF70

TYPE 06:

entry            = base + 0x84
target           = base + 0x5F8

vertex descriptor = base + 0x640
vertex count      = 0x3A8 = 936

vertex data       = 0x1600

Vertex data ends at:

0x8B00

Face stream:

face stream = 0x8B04

The beginning is:

13 25 00 04
...
08 34 40 ...
42 12 ...

There are also multiple TYPE 16 submeshes.


4. Vertex stream

The external reverse-engineering work on this format already established that the vertex/face data are encoded as multiple streams with 32-bit stream headers.

The known vertex stream example is:

00 6D 00 29

followed by:

00 12 00 00

and floating-point data.

The documented interpretation is that 0x0029 contains:

position = float3
normal   = float3

while another descriptor such as 0x0022 contains position-only data.

This agrees with the vertex buffers found in my samples.


5. Face stream

The known first stream is:

13 25 00 04

followed by RGBA data.

This is consistent with the previous reverse-engineering work which identified 0x25130004 as a face-colour stream.

The colour applies to following faces until another colour stream occurs.

The important part is the following encoded geometry streams.


6. 42 streams

The major unresolved part is the 42 stream.

Examples:

42 02 ...
42 0A ...
42 12 ...

The records following the 4-byte header consist of 16-bit values.

The important discovery is that these are not simply three fixed fields of

index, U, V

for the entire stream.

The role of the fields changes depending on the current state.


7. 42 0A — strong evidence for rotating index state

One very useful example is:

en78a00.mdl
face stream @ 0x1EB4

Header:

42 0A 00 70

Control:

00 09 FF F6

followed by 28 records.

The first records are:

0002 02DC 0143
0001 02DC 0133
0005 02F4 0143
0004 02F4 0133
0008 030C 0143
0007 030C 0133
000B 0324 0143
000A 0324 0133
000E 033C 0143
000D 033C 0133

Here the first field behaves as the vertex index.

Then:

FFF6 0000 02DC
0123 0002 02DC
0113 0003 02F4
0123 0005 02F4
0113 0006 030C
0123 0008 030C
...

The index has moved to another field.

Later:

0113 FFF6 000C
033C 0123 000D
033C 0133 0009
0324 0123 000A
...

The index has moved again.

So the evidence strongly suggests a state machine where the active index field can rotate:

field 0 -> field 1 -> field 2

and the other two fields contain texture-coordinate-related data.

This produces coherent box-like geometry when decoded with the appropriate state changes.


8. 42 12 — the remaining problem

The difficult streams are 42 12.

For example:

en05a00.mdl
face stream @ 0xCFC4

Header:

42 12 00 74

Control:

00 06 00 06

18 records:

0   000A 0391 0307
1   0000 0367 0369
2   0002 0367 0307
3   0001 0319 0369
4   0003 0319 0307
5   000B 02CB 0308
6   0004 0000 0367
7   0359 0005 0367
8   03D7 0001 0319
9   0359 0006 031A
10  03D6 0003 000B
11  0188 02D5 000A
12  01D6 02D5 0007
13  01AF 02B0 FFFB
14  000B 0188 02D5
15  0003 0188 0289
16  0007 01AF 02B0
17  0002 01D6 0288

The first several records clearly have a small first field corresponding to vertex indices, while the other fields are UV-like values.

But later records show the active index field changing.

For example:

13  01AF 02B0 FFFB

Here FFFB cannot simply be treated as a restart/index because the other values are clearly consistent with UV data.

Therefore:

FFFx is NOT universally a restart marker.

It can occur in a field which is not currently the index field.


9. Another 42 12 example

en05a00.mdl, stream around:

0xD304

contains:

42 12 00 74

with control:

00 06 00 0B

The beginning:

11 02D0 0302
03 0318 0302
01 0318 035F
02 0363 0302
00 0363 035E
0A 03AB 0302
08 03AA 035E
09 03EE 035F
0C 03A9 03D2
0D 03EF 03D2
04 0341 03FE
FFFB 0003 0187

then:

0278 0002 01D6
0275 0007 01AF
02A8 000A 01D6
02D3 000B 0188
02D5 0003 000A
03AC 0301 000B

Again, the active index field changes.

The final record is particularly interesting:

03AC 0301 000B

because the only value which fits the local vertex-index range is the third field.


10. Main 42 12 stream

The main en05a00 geometry stream begins at approximately:

0x8B14

and has:

control = 0x0175, 0x0004

The associated vertex count is:

936

There are 1332 records according to the current stream analysis.

A section around records 396-407 looks like:

396  FFF6 0083 02B3
397  0302 0086 030D
398  0302 0071 030D
399  035A 006E 036D
...
406  035A FFF5 0081
407  02B3 0302 0080

Again, this strongly suggests that the current index field is changing without necessarily having a simple explicit FFFx marker in the same field.


11. Known stream controls

Some confirmed examples:

en53:
42 02
control = (6, 13)
records = 21

en63:
42 02
control = (33, -18)
records = 105

en67:
42 02
control = (2, 3)
records = 4

en78:
42 0A
control = (9, -10)
records = 28

en05:
42 12
control = (373, 4)
records = 1332

en92:
42 02
control = (230, 3)
records = 1056

en91:
42 02
control = (297, -5)
records = 1300

en52:
42 0A
control = (52, 35)
records = 183

I don't yet want to claim that these control values are counts. They may instead be part of the state/decoder parameters.


12. Models tested

The same structure has been reproduced across multiple MDLs:

en05a00.mdl
en52a00.mdl
en53a00.mdl
en63a00.mdl
en67a00.mdl
en78a00.mdl
en81a00.mdl
en91a00.mdl
en92a00.mdl
en96a01.mdl
ob_603.mdl

Examples of exact vertex counts:

en05 = 936
en52 = 816
en78 TYPE06 = 8 / 4 / 4
en78 TYPE16 = 39
en91 = 1571
en92 = 1518

13. What I need

I would like help with a working Noesis Python importer, preferably something along these lines:

def registerNoesisTypes():
    handle = noesis.register("Resident Evil Code Veronica X HD MDL", ".mdl")
    noesis.setHandlerTypeCheck(handle, mdlCheckType)
    noesis.setHandlerLoadModel(handle, mdlLoadModel)
    return 1

The importer should:

  1. Locate MDL blocks.

  2. Parse the 0x34-byte entry table.

  3. Locate TYPE 0x06 / TYPE 0x16 geometry entries.

  4. Resolve the vertex descriptor.

  5. Read the exact vertex count.

  6. Decode the vertex stream.

  7. Decode the face stream.

  8. Correctly distinguish triangle lists and triangle strips.

  9. Decode the UV coordinates.

  10. Build the mesh with rapi.rpg....

  11. Produce a visible mesh in Noesis.

  12. Eventually expose materials/textures and skeleton/weights.

At this stage the priority is only geometry + UVs.


14. Important Noesis detail

I am using:

Noesis 4.474

The plugin environment has:

rapi.rpgCreateContext()
rapi.rpgConstructModel()

but does NOT have:

rapi.rpgDestroyContext()

So a plugin written for this environment should not call rpgDestroyContext().


15. Current state

The MDL table / mesh descriptors / vertex counts / vertex locations are already reproducible.

The remaining unknown is essentially:

How exactly does the 42 xx face/UV stream encode its current index-field/state, and how should all of its records be converted into Noesis triangles + UVs?

I would particularly appreciate either:

  • an explanation of the 42 02 / 42 0A / 42 12 stream bitfields/state machine, or

  • a working Noesis Python decoder for these streams.

Once this is solved, the rest of the model format should be considerably easier to implement.

FILES OF TEMPLATE: randalfcastro-tech/Resident-Evil-Code-Veronica-X-HD-PS3: Files of RECVXHD of PS3

Thanks!

Edited by Randalf2theReturn

  • Randalf2theReturn changed the title to Resident Evil Code Veronica X HD PS3 — MDL format reverse engineering / Noesis importer
  • Author

In addition to researching the MDL format, I have found two other related formats that I believe could be important for completing animation support for *Resident Evil Code: Veronica X HD* (PS3).

I have attached actual .RMT and .MTN files:

* rm_9100(1).rmt — 61,504 bytes

* en05ms(1).mtn — 369,152 bytes

I performed an initial hex inspection, and both use a structure that begins with:

00 00 XX XX 4D 54 4E 80

that is:

[size][MTN 80]

The .RMT file contains at least two consecutive MTN blocks:

offset 0x0000

size 0x34DC

magic MTN 80

field 0x0010000B

relative/size field 0x000034C0

offset 0x34E0

size 0xBB28

magic MTN 80

field 0x00100016

relative/size field 0x0000BB0C

The .MTN file contains many consecutive MTN blocks. For example:

0x0000 size 0x2B2C type 0x0010001C

0x2B30 size 0x0F0C type 0x0010001C

0x3A40 size 0x1598 type 0x0010001C

0x4FDC size 0x2B2C type 0x0010001C

...

The pattern repeats throughout the file. The first few bytes of en05ms.mtn are:

00 00 2B 2C

4D 54 4E 80

00 10 00 1C

00 00 2B 10

FF FF FF FF

00 00 00 00

C0 3F C2 A0

00 00 00 00

...

and those of rm_9100.rmt:

00 00 34 DC

4D 54 4E 80

00 10 00 0B

00 00 34 C0

FF FF FF FF

41 C8 00 00

41 E6 66 66

42 A0 00 00

...

The subsequent data contains numerous big-endian IEEE-754 values that appear potentially related to transformations/frames, although I do not yet want to claim they represent positions, rotations, or scales without a proven correlation.

My goal is not merely to create an importer for static MDL models. I would eventually like to achieve:

1. MDL → complete geometry.

2. MDL → materials/textures.

3. MDL → skeleton/bones.

4. MDL → weights/skin.

5. MTN/RMT → animations.

6. The ability to correctly associate an MTN/RMT animation with the corresponding MDL skeleton.

7. Importing all of this into Noesis and/or Blender while preserving the animations.

That is why I would like to know if anyone is familiar with the MTN 80 structure as well, specifically:

* the meaning of 0x0010000B, 0x00100016, and 0x0010001C; * the meaning of the DWORD located immediately after;

* the meaning of FFFFFFFF;

* the structure of the consecutive blocks;

* how frames/keyframes are stored;

* how bones/tracks are identified;

* whether .RMT is a container for one or multiple .MTN files;

* the relationship between .RMT.MTN filenames and .MDL models.

Files template: Resident-Evil-Code-Veronica-X-HD-PS3/Animation+modelsmdl at main · randalfcastro-tech/Resident-Evil-Code-Veronica-X-HD-PS3

I am simultaneously reverse-engineering the .MDL format for a Noesis importer, but I would also like to tackle .MTN.RMT from the start so that the final result can import models with skeletons, weights, and animations, rather than just the static mesh.

If anyone has documentation, legacy code, or extraction tools, or is familiar with these formats from any version of *Biohazard*/*Resident Evil Code: Veronica*, any information would be extremely helpful.

Edited by Randalf2theReturn

Create an account or sign in to comment

Account

Navigation

Search

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.