245 lines
7.3 KiB
Markdown
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
|