Skip to content

Comments

Correct vertex data reading for Unity < 4 meshes#366

Open
dv1x3r wants to merge 1 commit intoK0lb3:masterfrom
dv1x3r:mesh3
Open

Correct vertex data reading for Unity < 4 meshes#366
dv1x3r wants to merge 1 commit intoK0lb3:masterfrom
dv1x3r:mesh3

Conversation

@dv1x3r
Copy link
Contributor

@dv1x3r dv1x3r commented Feb 20, 2026

I fixed two issues in MeshHelper:

  1. get_channels: bytearray(n) creates n zero bytes not a bit array.
    Just a different behaviour if number is passed as an argument :D
    So I replaced it with bitwise comparison channelMask & (1 << i)

  2. read_vertex_data: skip processing if m_VertexCount or m_DataSize is empty.
    For compressed meshes, the m_VertexData.m_VertexCount is 0, and m_VertexData.m_DataSize is empty.
    UnityPy caused a crash in UnityPyBoost.unpack_vertexdata
    AssetStudio handles this gracefully because C# Buffer.BlockCopy silently copies 0 bytes when data is empty.

Sample: Login.unity3d.zip

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[14], line 26
     24 file_name = f"{obj.path_id}.{data.m_Name}.obj"
     25 file_path = os.path.join(export_path, env.file.name, "models", file_name)
---> 26 if wavefront_data := data.export():
     27     with open(file_path, "wt", newline="") as f:
     28         f.write(wavefront_data)

File ~/.venv/lib/python3.13/site-packages/UnityPy/classes/legacy_patch/Mesh.py:7, in _Mesh_export(self, format)
      4 def _Mesh_export(self: Mesh, format: str = "obj"):
      5     from ...export.MeshExporter import export_mesh
----> 7     return export_mesh(self, format)

File ~/.venv/lib/python3.13/site-packages/UnityPy/export/MeshExporter.py:13, in export_mesh(m_Mesh, format)
     11 def export_mesh(m_Mesh: Mesh, format: str = "obj") -> str:
     12     if format == "obj":
---> 13         return export_mesh_obj(m_Mesh)
     14     raise NotImplementedError(f"Export format {format} not implemented")

File ~/.venv/lib/python3.13/site-packages/UnityPy/export/MeshExporter.py:19, in export_mesh_obj(mesh, material_names)
     17 def export_mesh_obj(mesh: Mesh, material_names: Optional[List[str]] = None) -> str:
     18     handler = MeshHandler(mesh)
---> 19     handler.process()
     21     m_Mesh = handler
     22     if m_Mesh.m_VertexCount <= 0:

File ~/.venv/lib/python3.13/site-packages/UnityPy/helpers/MeshHelper.py:194, in MeshHandler.process(self)
    188     self.m_IndexBuffer = cast(
    189         List[int],
    190         struct.unpack(f"<{len(raw_indices) // index_size}{char}", raw_indices),
    191     )
    193 if self.version >= (3, 5):
--> 194     self.read_vertex_data(m_Channels, m_Streams)
    196 if isinstance(mesh, Mesh) and self.version >= (2, 6):
    197     self.decompress_compressed_mesh()

File ~/.venv/lib/python3.13/site-packages/UnityPy/helpers/MeshHelper.py:378, in MeshHandler.read_vertex_data(self, m_Channels, m_Streams)
    375 channel_dimension = m_Channel.dimension & 0xF
    377 if UnityPyBoost:
--> 378     componentBytes = UnityPyBoost.unpack_vertexdata(
    379         m_VertexData.m_DataSize,
    380         component_byte_size,
    381         m_VertexCount,
    382         m_Stream.offset,
    383         m_Stream.stride,
    384         m_Channel.offset,
    385         channel_dimension,
    386         swap,
    387     )
    388 else:
    389     componentBytes = bytearray(
    390         m_VertexCount * channel_dimension * component_byte_size
    391     )

ValueError: Vertex data access out of bounds

fix: correct channelMask bit parsing in get_channels
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant