[FIX] Client'ta map loading sırasında çökme (DDS yükleyici sınır kontrolü)

Kaptan Yosun

Co-Co Admin
Moderatör
Geliştirici
Yardımsever Üye
Usta Üye
Mesaj
1.586
Çözümler
57
Beğeni
3.171
Puan
1.839
Ticaret Puanı
0
Release client'ta ışınlanma sırasında çökme (DDS yükleyici sınır kontrolü)

Işınlanma sırasında Release client'ın bazen çökmesi, Debug client'ın ise hiç çökmemesi klasik bir tablodur. Sebep optimizasyon değil, iki derlemenin dosyaları farklı yerden okumasıdır.

  • UserInterface.vcxproj içinde _DISTRIBUTE sadece Release'de tanımlıdır. Bu da UserInterface.cpp içindeki bPackFirst değerini değiştirir.
  • Debug -> SEARCH_FILE_FIRST: dosyalar diskten, 0 offsetinden map'lenir. Dosyanın sonundan sonra tam bir sayfa (4 KB) okunabilir durumda kalır, dolayısıyla taşma sessizce yutulur.
  • Release -> SEARCH_PACK_FIRST: dosyalar .epk içinden, tam olarak girdinin boyutu kadar bir MapViewOfFile penceresiyle okunur. Verinin bittiği yerden sonraki ilk bayt map'lenmemiş bir sayfaya denk gelebilir -> 0xC0000005.

Yani hata her iki derlemede de vardır; sadece Release'de patlar. Çağrı yığını genelde şöyle biter:

Kod:
Genişlet Daralt Kopyala
memcpy
CDXTCImage::Copy               DXTCImage.cpp
CGraphicImageTexture::CreateDDSTexture
CGraphicImageTexture::CreateFromMemoryFile
CGraphicImage::OnLoad
CResource::Load
CMapOutdoor::CMapOutdoor       MapOutdoor.cpp
CMapManager::AllocMap
CPythonBackground::Create
CPythonNetworkStream::Warp

Düzeltilen sorunlar:
  1. LoadHeaderFromMemory her görüntü dosyasından (DDS olsun olmasın) koşulsuz 128 bayt okuyordu. fog.tga 76 bayttır; yani her TGA yüklemesinde 52 baytlık taşma oluyordu.
  2. "DDS " imzası kontrolü kaynakta yorum satırı yapılmıştı. DDS olmayan dosyalar DDS gibi ayrıştırılıyordu.
  3. Copy() hiçbir sınır kontrolü yapmadan m_lPitch >> (miplevel * 2) kadar bayt kopyalıyordu. Çöken satır budur.
  4. Mipmap boyut hesabı (dwLinearSize >>= 2) blok sıkıştırmanın 4x4 minimumunu yok sayıyordu; zincirin sonundaki seviyeler yanlış offsetten okunuyordu.
  5. CreateFromMemoryFile içindeki static CDXTCImage image, önceki dosyanın (artık yok olmuş) tamponuna işaret eden pointer'ları saklıyordu; ayrıca arka plan yükleme thread'i ile paylaşılıyordu.
  6. CEterPack::Get / Get2 içindeki out_file.Create() dönüş değeri kontrol edilmiyordu. Başarısız olursa *data hiç yazılmıyor, fonksiyon yine de true dönüyordu; CResource::Load içindeki fileData de ilk değer almamış olduğundan yükleyicilere çöp pointer gidiyordu.



EterImageLib/DXTCImage.h

aratın:
Genişlet Daralt Kopyala
    const BYTE* 		m_pbCompBufferByLevels[MAX_MIPLEVELS];
değiştirin:
Genişlet Daralt Kopyala
    const BYTE* 		m_pbCompBufferByLevels[MAX_MIPLEVELS];
    // Valid bytes behind each m_pbCompBufferByLevels entry, clamped to the
    // size of the buffer the header was parsed from.
    DWORD				m_adwMipSizes[MAX_MIPLEVELS];

aratın:
Genişlet Daralt Kopyala
    bool LoadFromMemory(const BYTE * c_pbMap);
    bool LoadHeaderFromMemory(const BYTE * c_pbMap);
    bool Copy(int miplevel, BYTE * pbDest, long lDestPitch);
değiştirin:
Genişlet Daralt Kopyala
    bool LoadFromMemory(const BYTE * c_pbMap, DWORD dwBufSize);
    bool LoadHeaderFromMemory(const BYTE * c_pbMap, DWORD dwBufSize);
    bool Copy(int miplevel, BYTE * pbDest, long lDestPitch, DWORD dwDestSize = 0xFFFFFFFF);

EterImageLib/DXTCImage.cpp

aratın:
Genişlet Daralt Kopyala
void CDXTCImage::Initialize()
{
    m_nWidth = 0;
    m_nHeight = 0;

    for (int i = 0; i < MAX_MIPLEVELS; ++i)
    {
        m_pbCompBufferByLevels[i] = NULL;
    }
}
değiştirin:
Genişlet Daralt Kopyala
void CDXTCImage::Initialize()
{
    m_nWidth = 0;
    m_nHeight = 0;

    m_nCompSize = 0;
    m_nCompLineSz = 0;
    m_strFormat[0] = '\0';
    m_CompFormat = PF_UNKNOWN;
    m_lPitch = 0;
    m_dwMipMapCount = 0;
    m_bMipTexture = false;
    m_dwFlags = 0;

    memset(&m_xddPixelFormat, 0, sizeof(m_xddPixelFormat));

    for (int i = 0; i < MAX_MIPLEVELS; ++i)
    {
        m_pbCompBufferByLevels[i] = NULL;
        m_adwMipSizes[i] = 0;
    }
}

aratın:
Genişlet Daralt Kopyala
    return LoadFromMemory((const BYTE*) pvMap);
değiştirin:
Genişlet Daralt Kopyala
    return LoadFromMemory((const BYTE*) pvMap, mappedFile.Size());

aratın:
Genişlet Daralt Kopyala
bool CDXTCImage::LoadHeaderFromMemory(const BYTE * c_pbMap)
{
    //////////////////////////////////////
    // start reading the file
    // from Microsoft's mssdk D3DIM example "Compress"
    DWORD dwMagic;

    // Read magic number
    dwMagic = *(DWORD*) c_pbMap;
    c_pbMap += sizeof(DWORD);

//!@#
//	if (dwMagic != MAKEFOURCC('D','D','S',' '))
//		return false;

    DDSURFACEDESC2 ddsd; // read from dds file

    // Read the surface description
    memcpy(&ddsd, c_pbMap, sizeof(DDSURFACEDESC2));
    c_pbMap += sizeof(DDSURFACEDESC2);

    // Does texture have mipmaps?
    m_bMipTexture = (ddsd.dwMipMapCount > 0) ? TRUE : FALSE;

    // Clear unwanted flags
    // Can't do this!!!  surface not re-created here
    //    ddsd.dwFlags &= (~DDSD_PITCH);
    //    ddsd.dwFlags &= (~DDSD_LINEARSIZE);

    // Is it DXTC ?
    // I sure hope pixelformat is valid!
    m_xddPixelFormat.dwFlags = ddsd.ddpfPixelFormat.dwFlags;
    m_xddPixelFormat.dwFourCC = ddsd.ddpfPixelFormat.dwFourCC;
    m_xddPixelFormat.dwSize = ddsd.ddpfPixelFormat.dwSize;
    m_xddPixelFormat.dwRGBBitCount = ddsd.ddpfPixelFormat.dwRGBBitCount;
    m_xddPixelFormat.dwRGBAlphaBitMask = ddsd.ddpfPixelFormat.dwRGBAlphaBitMask;
    m_xddPixelFormat.dwRBitMask = ddsd.ddpfPixelFormat.dwRBitMask;
    m_xddPixelFormat.dwGBitMask = ddsd.ddpfPixelFormat.dwGBitMask;
    m_xddPixelFormat.dwBBitMask = ddsd.ddpfPixelFormat.dwBBitMask;

    DecodePixelFormat(m_strFormat, &m_xddPixelFormat);

    if (m_CompFormat != PF_DXT1 &&
                        m_CompFormat != PF_DXT3 &&
                                        m_CompFormat != PF_DXT5)
    {
        return false;
    }

    if (ddsd.dwMipMapCount > MAX_MIPLEVELS)
    {
        ddsd.dwMipMapCount = MAX_MIPLEVELS;
    }

    m_nWidth		= ddsd.dwWidth;
    m_nHeight		= ddsd.dwHeight;
    //!@#
    m_dwMipMapCount = max(1, ddsd.dwMipMapCount);
    m_dwFlags		= ddsd.dwFlags;

    if (ddsd.dwFlags & DDSD_PITCH)
    {
        m_lPitch = ddsd.lPitch;
        m_pbCompBufferByLevels[0] = c_pbMap;
    }
    else
    {
        m_lPitch = ddsd.dwLinearSize;

        if (ddsd.dwFlags & DDSD_MIPMAPCOUNT)
        {
            for (DWORD dwLinearSize = ddsd.dwLinearSize, i = 0; i < m_dwMipMapCount; ++i, dwLinearSize >>= 2)
            {
                m_pbCompBufferByLevels[i] = c_pbMap;
                c_pbMap += dwLinearSize;
            }
        }
        else
        {
            m_pbCompBufferByLevels[0] = c_pbMap;
        }
    }

    return true;
}
değiştirin:
Genişlet Daralt Kopyala
// Bytes occupied by one mip level of a block-compressed surface. A DXT surface
// never shrinks below a single 4x4 block, so the old "linear size >>= 2 per
// level" shortcut is wrong for the tail of the chain.
static DWORD GetDXTLevelSize(EPixFormat compFormat, int nWidth, int nHeight, int nLevel)
{
    const DWORD dwBlockBytes = (PF_DXT1 == compFormat) ? 8 : 16;

    int nLevelWidth  = nWidth  >> nLevel;
    int nLevelHeight = nHeight >> nLevel;

    if (nLevelWidth < 1)
    {
        nLevelWidth = 1;
    }

    if (nLevelHeight < 1)
    {
        nLevelHeight = 1;
    }

    return (DWORD)((nLevelWidth + 3) / 4) * (DWORD)((nLevelHeight + 3) / 4) * dwBlockBytes;
}

bool CDXTCImage::LoadHeaderFromMemory(const BYTE * c_pbMap, DWORD dwBufSize)
{
    // Drop everything the previous file left behind first. A failed probe must
    // not leave this object holding pointers into a buffer that is already gone
    // (resource buffers are mapped views owned by a stack CMappedFile).
    Initialize();

    const DWORD c_dwHeaderSize = sizeof(DWORD) + sizeof(DDSURFACEDESC2);

    if (!c_pbMap || dwBufSize < c_dwHeaderSize)
    {
        return false;
    }

    //////////////////////////////////////
    // start reading the file
    // from Microsoft's mssdk D3DIM example "Compress"
    DWORD dwMagic;

    // Read magic number
    dwMagic = *(DWORD*) c_pbMap;
    c_pbMap += sizeof(DWORD);

    // This runs on every image the client loads, DDS or not, so the magic has to
    // be checked before the surface description is read out of the buffer.
    if (dwMagic != MAKEFOURCC('D', 'D', 'S', ' '))
    {
        return false;
    }

    DDSURFACEDESC2 ddsd; // read from dds file

    // Read the surface description
    memcpy(&ddsd, c_pbMap, sizeof(DDSURFACEDESC2));
    c_pbMap += sizeof(DDSURFACEDESC2);

    // Does texture have mipmaps?
    m_bMipTexture = (ddsd.dwMipMapCount > 0) ? TRUE : FALSE;

    // Clear unwanted flags
    // Can't do this!!!  surface not re-created here
    //    ddsd.dwFlags &= (~DDSD_PITCH);
    //    ddsd.dwFlags &= (~DDSD_LINEARSIZE);

    // Is it DXTC ?
    // I sure hope pixelformat is valid!
    m_xddPixelFormat.dwFlags = ddsd.ddpfPixelFormat.dwFlags;
    m_xddPixelFormat.dwFourCC = ddsd.ddpfPixelFormat.dwFourCC;
    m_xddPixelFormat.dwSize = ddsd.ddpfPixelFormat.dwSize;
    m_xddPixelFormat.dwRGBBitCount = ddsd.ddpfPixelFormat.dwRGBBitCount;
    m_xddPixelFormat.dwRGBAlphaBitMask = ddsd.ddpfPixelFormat.dwRGBAlphaBitMask;
    m_xddPixelFormat.dwRBitMask = ddsd.ddpfPixelFormat.dwRBitMask;
    m_xddPixelFormat.dwGBitMask = ddsd.ddpfPixelFormat.dwGBitMask;
    m_xddPixelFormat.dwBBitMask = ddsd.ddpfPixelFormat.dwBBitMask;

    DecodePixelFormat(m_strFormat, &m_xddPixelFormat);

    if (m_CompFormat != PF_DXT1 &&
        m_CompFormat != PF_DXT3 &&
        m_CompFormat != PF_DXT5)
    {
        return false;
    }

    if (0 == ddsd.dwWidth || 0 == ddsd.dwHeight)
    {
        return false;
    }

    if (ddsd.dwMipMapCount > MAX_MIPLEVELS)
    {
        ddsd.dwMipMapCount = MAX_MIPLEVELS;
    }

    m_nWidth  = ddsd.dwWidth;
    m_nHeight = ddsd.dwHeight;
    m_dwMipMapCount = max(1, ddsd.dwMipMapCount);
    m_dwFlags = ddsd.dwFlags;

    const DWORD c_dwDataSize = dwBufSize - c_dwHeaderSize;

    if (ddsd.dwFlags & DDSD_PITCH)
    {
        m_lPitch = ddsd.lPitch;
        m_dwMipMapCount = 1;
        m_pbCompBufferByLevels[0] = c_pbMap;
        m_adwMipSizes[0] = c_dwDataSize;
        return c_dwDataSize > 0;
    }

    m_lPitch = ddsd.dwLinearSize;

    const DWORD c_dwLevelCount = (ddsd.dwFlags & DDSD_MIPMAPCOUNT) ? m_dwMipMapCount : 1;

    DWORD dwOffset = 0;
    DWORD dwValidLevels = 0;

    for (DWORD i = 0; i < c_dwLevelCount; ++i)
    {
        if (dwOffset >= c_dwDataSize)
        {
            break;      // file is truncated - stop instead of running off the end
        }

        DWORD dwLevelSize = GetDXTLevelSize(m_CompFormat, m_nWidth, m_nHeight, (int) i);

        // Never hand out more bytes than the buffer actually holds. In a pack
        // build the buffer is a MapViewOfFile window sized to the entry, so one
        // byte past the end can be an unmapped page.
        if (dwLevelSize > c_dwDataSize - dwOffset)
        {
            dwLevelSize = c_dwDataSize - dwOffset;
        }

        m_pbCompBufferByLevels[i] = c_pbMap + dwOffset;
        m_adwMipSizes[i] = dwLevelSize;

        dwOffset += dwLevelSize;
        ++dwValidLevels;
    }

    if (0 == dwValidLevels)
    {
        return false;
    }

    m_dwMipMapCount = dwValidLevels;
    return true;
}

aratın:
Genişlet Daralt Kopyala
bool CDXTCImage::LoadFromMemory(const BYTE * c_pbMap)
{
    if (!LoadHeaderFromMemory(c_pbMap))
    {
        return false;
    }

    if (m_dwFlags & DDSD_PITCH)
    {
        DWORD dwBytesPerRow = m_nWidth * m_xddPixelFormat.dwRGBBitCount / 8;

        m_nCompSize = m_lPitch * m_nHeight;
        m_nCompLineSz = dwBytesPerRow;

        m_bCompVector[0].resize(m_nCompSize);
        BYTE * pDest = &m_bCompVector[0][0];

        c_pbMap = m_pbCompBufferByLevels[0];

        for (int yp = 0; yp < m_nHeight; ++yp)
        {
            memcpy(pDest, c_pbMap, dwBytesPerRow);
            pDest += m_lPitch;
            c_pbMap += m_lPitch;
        }
    }
    else
    {
        if (m_dwFlags & DDSD_MIPMAPCOUNT)
        {
            for (DWORD dwLinearSize = m_lPitch, i = 0; i < m_dwMipMapCount; ++i, dwLinearSize >>= 2)
            {
                m_bCompVector[i].resize(dwLinearSize);
                Copy(i, &m_bCompVector[i][0], dwLinearSize);
            }
        }
        else
        {
            m_bCompVector[0].resize(m_lPitch);
            Copy(0, &m_bCompVector[0][0], m_lPitch);
        }
    }

    // done reading file
    return true;
}
değiştirin:
Genişlet Daralt Kopyala
bool CDXTCImage::LoadFromMemory(const BYTE * c_pbMap, DWORD dwBufSize)
{
    if (!LoadHeaderFromMemory(c_pbMap, dwBufSize))
    {
        return false;
    }

    if (m_dwFlags & DDSD_PITCH)
    {
        DWORD dwBytesPerRow = m_nWidth * m_xddPixelFormat.dwRGBBitCount / 8;

        if (m_lPitch <= 0 || dwBytesPerRow > (DWORD) m_lPitch)
        {
            return false;
        }

        m_nCompSize = m_lPitch * m_nHeight;
        m_nCompLineSz = dwBytesPerRow;

        m_bCompVector[0].resize(m_nCompSize);
        BYTE * pDest = &m_bCompVector[0][0];

        const BYTE * c_pbSrc = m_pbCompBufferByLevels[0];
        DWORD dwAvailable = m_adwMipSizes[0];

        for (int yp = 0; yp < m_nHeight; ++yp)
        {
            if (dwAvailable < dwBytesPerRow)
            {
                break;      // truncated source - leave the remaining rows zeroed
            }

            memcpy(pDest, c_pbSrc, dwBytesPerRow);
            pDest += m_lPitch;
            c_pbSrc += m_lPitch;
            dwAvailable -= (dwAvailable < (DWORD) m_lPitch) ? dwAvailable : (DWORD) m_lPitch;
        }
    }
    else
    {
        for (DWORD i = 0; i < m_dwMipMapCount; ++i)
        {
            m_bCompVector[i].resize(m_adwMipSizes[i]);

            if (m_adwMipSizes[i])
            {
                Copy((int) i, &m_bCompVector[i][0], m_adwMipSizes[i], m_adwMipSizes[i]);
            }
        }
    }

    // done reading file
    return true;
}

aratın:
Genişlet Daralt Kopyala
bool CDXTCImage::Copy(int miplevel, BYTE * pbDest, long lDestPitch)
{
    if (!(m_dwFlags & DDSD_MIPMAPCOUNT))
        if (miplevel)
        {
            return false;
        }

    /*
    DXTColBlock * pBlock;
    WORD * pPos = (WORD *) &m_pbCompBufferByLevels[miplevel][0];
    int xblocks = (m_nWidth >> miplevel) / 4;
    int yblocks = (m_nHeight >> miplevel) / 4;

    for (int y = 0; y < yblocks; ++y)
    {
    	// 8 bytes per block
    	pBlock = (DXTColBlock*) ((DWORD) pPos + y * xblocks * 8);

    	memcpy(pbDest, pBlock, xblocks * 8);
    	pbDest += lDestPitch;
    }
    */

    memcpy(pbDest, m_pbCompBufferByLevels[miplevel], m_lPitch >> (miplevel * 2));
    pbDest += lDestPitch;
    return true;
}
değiştirin:
Genişlet Daralt Kopyala
bool CDXTCImage::Copy(int miplevel, BYTE * pbDest, long /*lDestPitch*/, DWORD dwDestSize)
{
    if (miplevel < 0 || miplevel >= MAX_MIPLEVELS)
    {
        return false;
    }

    if (!pbDest || !m_pbCompBufferByLevels[miplevel])
    {
        return false;
    }

    DWORD dwCopySize = m_adwMipSizes[miplevel];

    if (0 == dwCopySize)
    {
        return false;
    }

    // m_adwMipSizes was clamped to the source buffer when the header was read,
    // and dwDestSize bounds the surface we were handed, so this memcpy can no
    // longer walk off either end.
    if (dwCopySize > dwDestSize)
    {
        dwCopySize = dwDestSize;
    }

    memcpy(pbDest, m_pbCompBufferByLevels[miplevel], dwCopySize);
    return true;
}

EterLib/GrpImageTexture.cpp

aratın:
Genişlet Daralt Kopyala
    // 3. Fill the system memory staging texture
    for (DWORD i = 0; i < mipmapCount; ++i)
    {
        D3DLOCKED_RECT lockedRect;

        if (FAILED(lpd3dSysMemTexture->LockRect(i, &lockedRect, nullptr, 0)))
        {
            TraceError("CreateDDSTexture: Cannot lock system memory texture for mipmap %d", i);
            lpd3dSysMemTexture->Release();
            m_lpd3dTexture->Release();
            m_lpd3dTexture = nullptr;
            return false;
        }

        image.Copy(i, (BYTE*)lockedRect.pBits, lockedRect.Pitch);
        lpd3dSysMemTexture->UnlockRect(i);
    }
değiştirin:
Genişlet Daralt Kopyala
    // 3. Fill the system memory staging texture
    //    D3DX can hand back fewer levels than we asked for, and the source file
    //    may hold fewer still, so bound the loop by both.
    DWORD dwLevelCount = lpd3dSysMemTexture->GetLevelCount();

    if (dwLevelCount > (DWORD) mipmapCount)
    {
        dwLevelCount = (DWORD) mipmapCount;
    }

    for (DWORD i = 0; i < dwLevelCount; ++i)
    {
        D3DSURFACE_DESC levelDesc;

        if (FAILED(lpd3dSysMemTexture->GetLevelDesc(i, &levelDesc)))
        {
            TraceError("CreateDDSTexture: Cannot query system memory texture level %lu", i);
            lpd3dSysMemTexture->Release();
            m_lpd3dTexture->Release();
            m_lpd3dTexture = nullptr;
            return false;
        }

        D3DLOCKED_RECT lockedRect;

        if (FAILED(lpd3dSysMemTexture->LockRect(i, &lockedRect, nullptr, 0)))
        {
            TraceError("CreateDDSTexture: Cannot lock system memory texture for mipmap %lu", i);
            lpd3dSysMemTexture->Release();
            m_lpd3dTexture->Release();
            m_lpd3dTexture = nullptr;
            return false;
        }

        // For a block-compressed surface Pitch is the byte size of one row of
        // 4x4 blocks, so this is the size of the whole level.
        const DWORD dwDestSize = ((levelDesc.Height + 3) / 4) * (DWORD) lockedRect.Pitch;

        image.Copy((int) i, (BYTE*)lockedRect.pBits, lockedRect.Pitch, dwDestSize);
        lpd3dSysMemTexture->UnlockRect(i);
    }

aratın:
Genişlet Daralt Kopyala
    static CDXTCImage image;
değiştirin:
Genişlet Daralt Kopyala
    if (!c_pvBuf || 0 == bufSize)
    {
        TraceError("CreateFromMemoryFile: empty buffer for %s", m_stFileName.c_str());
        return false;
    }

    // Not static: this object holds pointers into c_pvBuf, which is only valid
    // for the duration of this call.
    CDXTCImage image;

aratın:
Genişlet Daralt Kopyala
if (image.LoadHeaderFromMemory((const BYTE*) c_pvBuf))
değiştirin:
Genişlet Daralt Kopyala
if (image.LoadHeaderFromMemory((const BYTE*) c_pvBuf, bufSize))

EterPack/EterPack.cpp
Aşağıdaki satır dosyada 2 kez geçer (Get ve Get2 içinde). Her ikisini de değiştirin.

aratın:
Genişlet Daralt Kopyala
    out_file.Create(m_stDataFileName.c_str(), data, index->data_position, index->data_size);
değiştirin:
Genişlet Daralt Kopyala
    // Must be checked: on failure *data is never written, and the callers
    // hand that pointer straight to the resource loaders.
    if (!out_file.Create(m_stDataFileName.c_str(), data, index->data_position, index->data_size))
    {
        TraceError("Failed to map pack data : %s (pos %ld, size %ld)", filename, index->data_position, index->data_size);
        return false;
    }

EterLib/Resource.cpp
Aşağıdaki satır dosyada 2 kez geçer (Load ve Reload içinde). Her ikisini de değiştirin.

aratın:
Genişlet Daralt Kopyala
    LPCVOID		fileData;
değiştirin:
Genişlet Daralt Kopyala
    LPCVOID		fileData = NULL;
 
Son düzenleme:
bu çözümü bulmasaydım windows sf projesi iptal olacaktı delirecektim valla çok teşekkürler
 
Eline sağlık dostum gerçekten zorlu bir hataydı birde sys vb vermiyor anlamak çok güç. teşekkürler.
 
Konu güncellendi. Eski paylaştığım fix 5 problemden sadece 1 tanesinin fixiydi. Bu yeni sürümü yapmanızı tavsiye ederim.
 
Geri
Üst