5 hours ago5 hr 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 blocksThe files contain blocks identified by:4D 44 4C BCand sometimes:4D 44 4C ADThe MDL blocks contain a table of entries.Each entry is:0x34 bytesThe important fields identified so far are:entry + 0x08 = entry type entry + 0x0C = relative targetThe relative offsets are relative to the MDL block.2. TYPE 0x06 / TYPE 0x16 mesh entriesTYPE 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 descriptorVertex descriptor:VD = 0x3E0 relative vertex count = 0x330 = 816 vertex data = VD + 0x50Therefore:vertex data abs = 0x1180The vertex data occupies:0x1180 ... 0x7783and the face stream begins at:0x7784The face-stream pointer itself is:target + 0x14with the actual stream beginning at:face_pointer + 0x10The same structure was observed in several independent models.3. Confirmed examplesen92a00.mdlMDL 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 = 0xDD04Face stream starts:13 25 00 04 7F 7F 7F FF 7F 7F 7F FF 08 34 40 09 42 02 18 C4 ...en91a00.mdlMDL 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 = 0xE4A4Face stream:13 25 00 04 ... 42 02 1E 80 ...en05a00.mdlLibrary sample:file size = 58560 bytes MDL base = 0xF70TYPE 06:entry = base + 0x84 target = base + 0x5F8 vertex descriptor = base + 0x640 vertex count = 0x3A8 = 936 vertex data = 0x1600Vertex data ends at:0x8B00Face stream:face stream = 0x8B04The beginning is:13 25 00 04 ... 08 34 40 ... 42 12 ...There are also multiple TYPE 16 submeshes.4. Vertex streamThe 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 29followed by:00 12 00 00and floating-point data.The documented interpretation is that 0x0029 contains:position = float3 normal = float3while another descriptor such as 0x0022 contains position-only data.This agrees with the vertex buffers found in my samples.5. Face streamThe known first stream is:13 25 00 04followed 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 streamsThe 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 ofindex, U, Vfor the entire stream.The role of the fields changes depending on the current state.7. 42 0A — strong evidence for rotating index stateOne very useful example is:en78a00.mdl face stream @ 0x1EB4Header:42 0A 00 70Control:00 09 FF F6followed 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 0133Here 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 2and 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 problemThe difficult streams are 42 12.For example:en05a00.mdl face stream @ 0xCFC4Header:42 12 00 74Control:00 06 00 0618 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 0288The 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 FFFBHere 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 exampleen05a00.mdl, stream around:0xD304contains:42 12 00 74with control:00 06 00 0BThe 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 0187then:0278 0002 01D6 0275 0007 01AF 02A8 000A 01D6 02D3 000B 0188 02D5 0003 000A 03AC 0301 000BAgain, the active index field changes.The final record is particularly interesting:03AC 0301 000Bbecause the only value which fits the local vertex-index range is the third field.10. Main 42 12 streamThe main en05a00 geometry stream begins at approximately:0x8B14and has:control = 0x0175, 0x0004The associated vertex count is:936There 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 0080Again, 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 controlsSome 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 = 183I don't yet want to claim that these control values are counts. They may instead be part of the state/decoder parameters.12. Models testedThe 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.mdlExamples of exact vertex counts:en05 = 936 en52 = 816 en78 TYPE06 = 8 / 4 / 4 en78 TYPE16 = 39 en91 = 1571 en92 = 151813. What I needI 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 1The importer should:Locate MDL blocks.Parse the 0x34-byte entry table.Locate TYPE 0x06 / TYPE 0x16 geometry entries.Resolve the vertex descriptor.Read the exact vertex count.Decode the vertex stream.Decode the face stream.Correctly distinguish triangle lists and triangle strips.Decode the UV coordinates.Build the mesh with rapi.rpg....Produce a visible mesh in Noesis.Eventually expose materials/textures and skeleton/weights.At this stage the priority is only geometry + UVs.14. Important Noesis detailI am using:Noesis 4.474The 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 stateThe 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, ora 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 PS3Thanks! Edited 5 hours ago5 hr by Randalf2theReturn
Create an account or sign in to comment