Skip to content

Conversation

@isHarryh
Copy link
Contributor

Summary

Fixes #333

Test

Tested with the sample file provided in #333 .

(Left: AssetStudio, Right: Ours)

image

Copy link
Owner

@K0lb3 K0lb3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, there is just a little extra optimization I would like for this special case.

@mos9527
Copy link
Contributor

mos9527 commented Jul 20, 2025

@K0lb3 Decompilation from 2022.3.21f1 development DLL reveals quite some changes from the Unity 4 reference code - TL;DR in such cases we probably should return an array of m_Start instead of NaN. Here's the annotated pesudocode for reference.

void __fastcall PackedFloatVector::UnpackFloats(
        PackedFloatVector *this,
        float *data,
        int itemCountInChunk,
        int chunkStride,
        int start,
        int numChunks)
{
  __int64 itemCountInChunk_1; // r10
  int m_BitSize; // ecx
  float *data_1; // r15
  double scale; // xmm2_8
  int indexPos; // ebx
  int bitPos; // r8d
  unsigned int numChunks_1; // eax
  __int64 itemCountInChunk_2; // r12
  float *end; // r13
  __int64 chunkStride_1; // rax
  __int64 indexPos_1; // r9
  __int64 i; // rsi
  int m_BitSize_1; // r10d
  int x; // edi
  int bits; // r11d
  int num; // ecx
  __int64 indexPos_pp; // rax
  int v24; // edx
  int v25; // eax
  __int64 chunkStride_2; // [rsp+30h] [rbp+8h]

  itemCountInChunk_1 = itemCountInChunk;
  m_BitSize = this->m_BitSize;
  data_1 = data;
  scale = this->m_Range;
  indexPos = start * m_BitSize / 8;
  bitPos = start * m_BitSize % 8;
  // !! scale div by 0 skipped if m_BitSize is 0
  // -> scale = m_Range
  if ( m_BitSize )
    scale = scale / ((1 << m_BitSize) - 1);
  numChunks_1 = numChunks;
  if ( numChunks == -1 )
    numChunks_1 = this->m_NumItems / itemCountInChunk_1;
  itemCountInChunk_2 = itemCountInChunk_1;
  end = (data + (numChunks_1 * chunkStride));
  if ( data != end )
  {
    chunkStride_1 = chunkStride;
    chunkStride_2 = chunkStride;
    indexPos_1 = indexPos;
    do
    {
      i = 0i64;
      if ( itemCountInChunk_2 > 0 )
      {
        do
        {
          m_BitSize_1 = this->m_BitSize;
          x = 0;
          bits = 0;
          if ( this->m_BitSize )
          {
            do
            {
              x |= *(indexPos_1 + this->data) >> bitPos << bits;
              num = m_BitSize_1 - bits;
              if ( 8 - bitPos < m_BitSize_1 - bits )
                num = 8 - bitPos;
              indexPos_pp = indexPos_1 + 1;
              bits += num;
              v24 = num + bitPos;               // std::min( m_BitSize-bits, 8-bitPos)
              if ( num + bitPos != 8 )
                indexPos_pp = indexPos_1;       // indexPos++
              indexPos_1 = indexPos_pp;
              v25 = indexPos + 1;
              if ( v24 != 8 )
                v25 = indexPos;
              bitPos = 0;
              indexPos = v25;
              if ( v24 != 8 )
                bitPos = v24;
            }
            while ( bits < m_BitSize_1 );
          }                                     // !! Jump here if m_BitSize is 0
                                                // -> data[i] = 0 * scale + this->m_Start
                                                // -> data[i] = this->m_Start
          data_1[i++] = (x & ((1 << m_BitSize_1) - 1)) * scale + this->m_Start;
        }
        while ( i < itemCountInChunk_2 );
        chunkStride_1 = chunkStride_2;
      }
      data_1 = (data_1 + chunkStride_1);
    }
    while ( data_1 != end );
  }
}

It should also be noted that it's impossible to produce m_BitSize = 0 blobs in Unity 4.

@isHarryh
Copy link
Contributor Author

I agree with @mos9527 , we probably should return an array of m_Start here.

@isHarryh isHarryh requested a review from K0lb3 July 20, 2025 12:25
@K0lb3
Copy link
Owner

K0lb3 commented Aug 1, 2025

Sorry for my lack of reply.

I was thinking about the issue today and talked it from a different perspective, how a bitsize of 0 could occur.
A bitsize of 0 can only occur if the max and min value of the are identical, so if the array only has identical elements.
In that case all elements would be 0 after the smalled element gets substracted as offset from each element.
This offset is stored as m_Start.

So it certainly makes sense to simply assume an array of [m_Start]*m_NumItems as decoded result (before reshaping).
You probably had similar ideas as well.

@K0lb3 K0lb3 merged commit 536ec78 into K0lb3:master Aug 8, 2025
6 checks passed
K0lb3 pushed a commit that referenced this pull request Aug 9, 2025
* fix(PackedBitVector): zero division error
K0lb3 pushed a commit that referenced this pull request Aug 11, 2025
* fix(PackedBitVector): zero division error
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.

PackedBitVector.unpack_floats: ZeroDivisionError

3 participants