Files
test/Documentation/ConfigVersion_Analysis.md
2026-03-13 16:03:47 +07:00

245 lines
7.3 KiB
Markdown

# Config Version Mismatch Analysis
## Problem Summary
When `LoadConfigsFromServer` calls `LoadUserConfigData`, the C# version throws an exception about version mismatch (`dwVer > EC_CONFIG_VERSION`).
## Root Cause Analysis
### Data Flow Comparison
#### C++ Version (EC_GameRun.cpp lines 2067-2169)
```cpp
bool CECGameRun::LoadConfigsFromServer(const void* pDataBuf, int iDataSize)
{
// 1. Read USERCFG_VERSION (version 3)
DWORD dwVer = *(DWORD*)pData;
pData += sizeof (DWORD);
if (dwVer > USERCFG_VERSION) // USERCFG_VERSION = 3
{
return false;
}
// 2. Uncompress data if version >= 3
if (dwVer >= 3)
{
dwRealLen = 4096;
pUncompBuf = a_malloctemp(dwRealLen);
AFilePackage::Uncompress(pData, iDataSize-sizeof(DWORD), pUncompBuf, &dwRealLen);
pData = (BYTE*)pUncompBuf; // Point to uncompressed data
}
// 3. Create data reader with uncompressed data
CECDataReader dr(pData, (int)dwRealLen);
// 4. Read host configs
int iSize = dr.Read_int();
pHost->LoadConfigData(dr.Read_Data(iSize));
// 5. Read UI configs
iSize = dr.Read_int();
pGameUI->SetUserLayout(dr.Read_Data(iSize), iSize);
// 6. Read user settings (if dwVer >= 2)
if (dwVer >= 2)
{
iSize = dr.Read_int();
g_pGame->GetConfigs()->LoadUserConfigData(dr.Read_Data(iSize), iSize);
// This data starts with EC_CONFIG_VERSION (36)
}
}
```
#### C# Version (CECGameRun.cs lines 273-392)
```csharp
public bool LoadConfigsFromServer(byte[] pDataBuf, int iDataSize)
{
const uint USERCFG_VERSION = 3;
int offset = 0;
// 1. Read USERCFG_VERSION
uint dwVer = System.BitConverter.ToUInt32(pDataBuf, offset);
offset += sizeof(uint);
if (dwVer > USERCFG_VERSION) // USERCFG_VERSION = 3
{
Debug.LogError($"version {dwVer} > {USERCFG_VERSION}");
return false;
}
byte[] pUncompBuf = null;
uint dwRealLen = (uint)(iDataSize - sizeof(uint));
byte[] pData = pDataBuf;
int dataOffset = offset; // ⚠️ This is set but never used!
// 2. Uncompress if version >= 3
if (dwVer >= 3)
{
dwRealLen = 4096;
pUncompBuf = new byte[dwRealLen];
byte[] compressedData = new byte[iDataSize - sizeof(uint)];
System.Array.Copy(pDataBuf, offset, compressedData, 0, compressedData.Length);
int iRes = AFilePackage.Uncompress(compressedData, compressedData.Length,
pUncompBuf, ref dwRealLen);
if (iRes != 0)
{
return false;
}
pData = pUncompBuf; // ⚠️ Point to uncompressed buffer
}
// 3. Create data reader - ⚠️ PROBLEM HERE!
// In C++, pData points to the uncompressed data
// In C#, we should use the same approach
CECDataReader dr = new CECDataReader(pData, (int)dwRealLen);
// 4. Read host configs
int iSize = dr.ReadInt();
byte[] hostConfigData = dr.ReadData(iSize);
pHost.LoadConfigData(hostConfigData);
// 5. Read UI configs
iSize = dr.ReadInt();
byte[] uiConfigData = dr.ReadData(iSize);
// 6. Read user settings
if (dwVer >= 2)
{
iSize = dr.ReadInt();
byte[] settingsData = dr.ReadData(iSize);
// ⚠️ HERE IS WHERE THE ERROR OCCURS
if (!EC_Game.GetConfigs().LoadUserConfigData(settingsData, iSize))
{
return false;
}
}
}
```
### LoadUserConfigData Comparison
#### C++ Version (EC_Configs.cpp lines 628-671)
```cpp
bool CECConfigs::LoadUserConfigData(const void* pDataBuf, int iDataSize)
{
CECDataReader dr((void*)pDataBuf, iDataSize);
// Read EC_CONFIG_VERSION (should be 36)
DWORD dwVer = dr.Read_DWORD();
if (dwVer < 15)
{
DefaultUserConfigData();
goto End;
}
else if (dwVer > EC_CONFIG_VERSION) // EC_CONFIG_VERSION = 36
{
throw CECException(CECException::TYPE_DATAERR);
}
m_vs.Read(dr, dwVer);
m_gs.Read(dr, dwVer);
m_bs.Read(dr, dwVer);
m_cas.Read(dr, dwVer);
}
```
#### C# Version (EC_Configs.cs lines 1070-1106)
```csharp
public bool LoadUserConfigData(byte[] pDataBuf, int iDataSize)
{
using (MemoryStream ms = new MemoryStream(pDataBuf, 0, iDataSize))
using (BinaryReader reader = new BinaryReader(ms))
{
// Read EC_CONFIG_VERSION (expecting 36)
uint dwVer = reader.ReadUInt32();
if (dwVer < 15)
{
DefaultUserConfigData();
goto End;
}
else if (dwVer > EC_ConfigConstants.EC_CONFIG_VERSION) // 36
{
throw new Exception("version mismatch dwVer=" + dwVer);
}
m_vs.Read(reader, dwVer);
m_gs.Read(reader, dwVer);
m_bs.Read(reader, dwVer);
m_cas.Read(reader, dwVer);
}
}
```
## The Actual Problem
The issue is that **`settingsData` does NOT start with a proper version number**. When `LoadUserConfigData` tries to read the first 4 bytes as `dwVer`, it's reading garbage data that happens to be larger than 36.
## Possible Causes
### 1. CECDataReader.ReadData() Implementation Issue
The C# `CECDataReader.ReadData(int size)` method may not be returning the correct data. Let me check if this method exists and how it's implemented.
### 2. Data Alignment/Packing Issue
The structs being saved in C++ may have different memory layout than the C# structs due to packing/alignment differences.
### 3. SaveConfigsToServer Format Issue
The data being saved might not match the expected format. The C++ version (lines 2040-2063) shows:
- Version is NOT compressed
- Only the config data after version is compressed
- But when loading, after uncompressing, the data should contain all three config sections
## Solution Steps
1. **Add Debug Logging**: Log the first 16 bytes of `settingsData` to see what version number is being read
2. **Verify CECDataReader**: Ensure `ReadData()` returns the correct bytes
3. **Check Struct Sizes**: Verify that `Marshal.SizeOf()` for each struct matches the C++ `sizeof()`
4. **Verify Decompression**: Ensure the uncompressed data length matches expectations
## Immediate Fix
Add debug logging in CECGameRun.cs after line 377:
```csharp
byte[] settingsData = dr.ReadData(iSize);
// DEBUG: Log the first 16 bytes
BMLogger.LogError($"LoadConfigsFromServer - settingsData size: {iSize}, first 16 bytes: " +
$"{BitConverter.ToString(settingsData.Take(Math.Min(16, settingsData.Length)).ToArray())}");
// DEBUG: Read the version to see what we're getting
uint debugVer = System.BitConverter.ToUInt32(settingsData, 0);
BMLogger.LogError($"LoadConfigsFromServer - Version read from settingsData: {debugVer} (expected <= 36)");
```
This will help identify what data is actually being passed to `LoadUserConfigData`.
## Expected Data Format
After uncompression, the data should be:
```
[4 bytes: host config size] [host config data...]
[4 bytes: UI config size] [UI config data...]
[4 bytes: user config size] [user config data...]
↑ This should start with EC_CONFIG_VERSION (36)
```
If the version being read is > 36, it means either:
1. The data reader is at the wrong position
2. The size being read is incorrect
3. The data format from server doesn't match expectations