diff --git a/analysis/ghidra/exports/Lmx.dll.set-attribute-result-xrefs.md b/analysis/ghidra/exports/Lmx.dll.set-attribute-result-xrefs.md new file mode 100644 index 0000000..0c40d7e --- /dev/null +++ b/analysis/ghidra/exports/Lmx.dll.set-attribute-result-xrefs.md @@ -0,0 +1,35 @@ +# Lmx.dll xrefs + +## 0x114a90 at 10114a90 + +Target function: `FUN_10114a90` + +| From | Ref type | Caller function | +| --- | --- | --- | +| `10196410` | `DATA` | `` | + +## 0x100dc750 at 100dc750 + +Target function: `FUN_100dc750` + +| From | Ref type | Caller function | +| --- | --- | --- | +| `1018f268` | `DATA` | `` | + +## 0x1010b990 at 1010b990 + +Target function: `FUN_1010b990` + +| From | Ref type | Caller function | +| --- | --- | --- | +| `1010cf49` | `UNCONDITIONAL_CALL` | `FUN_1010bd10` | +| `1010e440` | `UNCONDITIONAL_CALL` | `FUN_1010e410` | + +## 0x1010dc80 at 1010dc80 + +Target function: `FUN_1010dc80` + +| From | Ref type | Caller function | +| --- | --- | --- | +| `10195488` | `DATA` | `` | + diff --git a/analysis/ghidra/exports/Lmx.dll.synthesizer-callers-xrefs.md b/analysis/ghidra/exports/Lmx.dll.synthesizer-callers-xrefs.md new file mode 100644 index 0000000..a8e3dec --- /dev/null +++ b/analysis/ghidra/exports/Lmx.dll.synthesizer-callers-xrefs.md @@ -0,0 +1,119 @@ +# Lmx.dll xrefs + +## 0x1010bd10 at 1010bd10 + +Target function: `FUN_1010bd10` + +| From | Ref type | Caller function | +| --- | --- | --- | +| `1010d89b` | `UNCONDITIONAL_CALL` | `FUN_1010d4a0` | + +## 0x1010e410 at 1010e410 + +Target function: `FUN_1010e410` + +| From | Ref type | Caller function | +| --- | --- | --- | +| `101956a8` | `DATA` | `` | + +## 0x10101360 at 10101360 + +Target function: `FUN_10101360` + +| From | Ref type | Caller function | +| --- | --- | --- | +| `10061e82` | `UNCONDITIONAL_CALL` | `FUN_10061c60` | +| `10110335` | `UNCONDITIONAL_CALL` | `` | + +## 0x10100ce0 at 10100ce0 + +Target function: `FUN_10100ce0` + +| From | Ref type | Caller function | +| --- | --- | --- | +| `1010c2ea` | `UNCONDITIONAL_CALL` | `FUN_1010bd10` | +| `1010c474` | `UNCONDITIONAL_CALL` | `FUN_1010bd10` | +| `1010c50d` | `UNCONDITIONAL_CALL` | `FUN_1010bd10` | +| `1010c5fb` | `UNCONDITIONAL_CALL` | `FUN_1010bd10` | +| `1010c8ac` | `UNCONDITIONAL_CALL` | `FUN_1010bd10` | +| `1010ca5f` | `UNCONDITIONAL_CALL` | `FUN_1010bd10` | +| `1010cb16` | `UNCONDITIONAL_CALL` | `FUN_1010bd10` | +| `1010cd61` | `UNCONDITIONAL_CALL` | `FUN_1010bd10` | +| `1010f27d` | `UNCONDITIONAL_CALL` | `FUN_1010ee00` | +| `1010f365` | `UNCONDITIONAL_CALL` | `FUN_1010ee00` | +| `1010fa8d` | `UNCONDITIONAL_CALL` | `FUN_1010ee00` | +| `1010facf` | `UNCONDITIONAL_CALL` | `FUN_1010ee00` | + +## 0x10100bc0 at 10100bc0 + +Target function: `FUN_10100bc0` + +| From | Ref type | Caller function | +| --- | --- | --- | +| `1010c47e` | `UNCONDITIONAL_CALL` | `FUN_1010bd10` | +| `1010c605` | `UNCONDITIONAL_CALL` | `FUN_1010bd10` | +| `1010ca69` | `UNCONDITIONAL_CALL` | `FUN_1010bd10` | +| `1010cb20` | `UNCONDITIONAL_CALL` | `FUN_1010bd10` | + +## 0x1005e580 at 1005e580 + +Target function: `FUN_1005e580` + +| From | Ref type | Caller function | +| --- | --- | --- | +| `1010c612` | `UNCONDITIONAL_CALL` | `FUN_1010bd10` | +| `1010ca76` | `UNCONDITIONAL_CALL` | `FUN_1010bd10` | +| `1010f51a` | `UNCONDITIONAL_CALL` | `FUN_1010ee00` | +| `1010f8bb` | `UNCONDITIONAL_CALL` | `FUN_1010ee00` | +| `1010fa76` | `UNCONDITIONAL_CALL` | `FUN_1010ee00` | +| `1010fab8` | `UNCONDITIONAL_CALL` | `FUN_1010ee00` | +| `10110415` | `UNCONDITIONAL_CALL` | `FUN_1010ee00` | +| `10110440` | `UNCONDITIONAL_CALL` | `FUN_1010ee00` | +| `10110513` | `UNCONDITIONAL_CALL` | `FUN_1010ee00` | +| `101116fc` | `UNCONDITIONAL_CALL` | `` | +| `10111ab6` | `UNCONDITIONAL_CALL` | `` | +| `10111d51` | `UNCONDITIONAL_CALL` | `` | +| `10111884` | `UNCONDITIONAL_CALL` | `` | +| `101118d2` | `UNCONDITIONAL_CALL` | `` | +| `10111b52` | `UNCONDITIONAL_CALL` | `` | +| `10110be2` | `UNCONDITIONAL_CALL` | `FUN_10110986` | +| `10110c03` | `UNCONDITIONAL_CALL` | `FUN_10110986` | + +## 0x10067aa0 at 10067aa0 + +Target function: `FUN_10067aa0` + +| From | Ref type | Caller function | +| --- | --- | --- | +| `1006afb2` | `UNCONDITIONAL_CALL` | `FUN_10069c30` | +| `101044a9` | `UNCONDITIONAL_CALL` | `Catch@10104467` | +| `100fd351` | `UNCONDITIONAL_CALL` | `FUN_100fd200` | +| `10067d89` | `UNCONDITIONAL_CALL` | `FUN_10067d30` | +| `100fd560` | `UNCONDITIONAL_CALL` | `FUN_100fd400` | +| `100ffe3a` | `UNCONDITIONAL_CALL` | `FUN_100ffc90` | +| `1010951d` | `UNCONDITIONAL_CALL` | `FUN_10107880` | +| `1010bfcc` | `UNCONDITIONAL_CALL` | `FUN_1010bd10` | +| `1010c250` | `UNCONDITIONAL_CALL` | `FUN_1010bd10` | +| `1010c2bb` | `UNCONDITIONAL_CALL` | `FUN_1010bd10` | +| `1010c7a4` | `UNCONDITIONAL_CALL` | `FUN_1010bd10` | +| `1010d27a` | `UNCONDITIONAL_CALL` | `FUN_1010bd10` | +| `1010f497` | `UNCONDITIONAL_CALL` | `FUN_1010ee00` | +| `10070743` | `UNCONDITIONAL_CALL` | `FUN_10070360` | +| `10070869` | `UNCONDITIONAL_CALL` | `FUN_10070360` | +| `10070a01` | `UNCONDITIONAL_CALL` | `FUN_10070360` | + +## 0x100860c0 at 100860c0 + +Target function: `FUN_100860c0` + +| From | Ref type | Caller function | +| --- | --- | --- | +| `10069f19` | `UNCONDITIONAL_CALL` | `FUN_10069c30` | +| `1006a588` | `UNCONDITIONAL_CALL` | `FUN_10069c30` | +| `10138a4b` | `UNCONDITIONAL_CALL` | `FUN_101389c0` | +| `1010c158` | `UNCONDITIONAL_CALL` | `FUN_1010bd10` | +| `1010c206` | `UNCONDITIONAL_CALL` | `FUN_1010bd10` | +| `1010c5ba` | `UNCONDITIONAL_CALL` | `FUN_1010bd10` | +| `1010d0b4` | `UNCONDITIONAL_CALL` | `FUN_1010bd10` | +| `1010d167` | `UNCONDITIONAL_CALL` | `FUN_1010bd10` | + diff --git a/analysis/ghidra/exports/Lmx.dll.synthesizer-decompile.md b/analysis/ghidra/exports/Lmx.dll.synthesizer-decompile.md new file mode 100644 index 0000000..1283296 --- /dev/null +++ b/analysis/ghidra/exports/Lmx.dll.synthesizer-decompile.md @@ -0,0 +1,1149 @@ +# Lmx.dll selected decompile + +## FUN_1010b990 at 1010b990 + +Signature: `undefined FUN_1010b990(void)` + +```c + +void __thiscall FUN_1010b990(undefined4 *param_1,char param_2,short *param_3,int *param_4) + +{ + int *piVar1; + char cVar2; + undefined4 uVar3; + basic_ostream_> *pbVar4; + int iVar5; + short *psVar6; + int *piVar7; + int *piVar8; + int *piVar9; + int *piVar10; + wchar_t *pwVar11; + undefined4 *puVar12; + _func_basic_ostream_>_ptr_basic_ostream_>_ptr + *p_Var13; + uint local_224; + wchar_t local_220 [260]; + short local_18 [2]; + uint local_14; + uint local_10; + undefined2 local_c; + uint local_8; + + local_8 = DAT_101d60b8 ^ (uint)&stack0xfffffffc; + if (param_2 == '\0') { + cVar2 = FUN_100408d0(); + if (cVar2 == '\0') goto LAB_1010bb4d; + uVar3 = *(undefined4 *)(DAT_101d6474 + 0x44); + puVar12 = param_1 + 5; + pwVar11 = L"ScanOnDemandCallback::MultipleOperationComplete with sameStatus FALSE - destEngine " + ; + p_Var13 = endl_exref; + } + else { + cVar2 = FUN_100408d0(); + if (cVar2 != '\0') { + swprintf_s(local_220,0x104,L"", + (int)(short)*(undefined4 *)param_3,*(undefined4 *)(param_3 + 2), + *(undefined4 *)(param_3 + 4),(int)param_3[6]); + p_Var13 = endl_exref; + uVar3 = FUN_1001a0e0(*(undefined4 *)(DAT_101d6474 + 0x44), + L"ScanOnDemandCallback::MultipleOperationComplete with sameStatus TRUE - mxStatus " + ,local_220,L" destEngine ",param_1 + 5); + uVar3 = FUN_1001a0e0(uVar3); + uVar3 = FUN_1001a0e0(uVar3); + pbVar4 = (basic_ostream_> *) + FUN_1001db00(uVar3); + std::basic_ostream_>::operator<< + (pbVar4,p_Var13); + } + if (((*param_3 != 0) || (*(int *)(param_3 + 2) != 3)) || + ((param_3[6] != 2 && (param_3[6] != 1)))) goto LAB_1010bb4d; + if ((int)param_1[0xf] < 4) { + param_1[0xf] = param_1[0xf] + 1; + FUN_1010ad00(0,param_1 + 5,param_1[9],param_1 + 10,0,2,param_1,0,0,0,0); + __security_check_cookie(local_8 ^ (uint)&stack0xfffffffc); + return; + } + cVar2 = FUN_100408d0(); + if (cVar2 == '\0') goto LAB_1010bb4d; + puVar12 = param_1 + 5; + pwVar11 = L" message to destEngine "; + p_Var13 = endl_exref; + uVar3 = FUN_1001a0e0(*(undefined4 *)(DAT_101d6474 + 4), + L"ScanOnDemandCallback::OperationComplete - Send retry limit reached for ", + param_1[9],L" message to destEngine ",puVar12); + uVar3 = FUN_100a68d0(uVar3); + } + uVar3 = FUN_1001a0e0(uVar3,pwVar11,puVar12); + pbVar4 = (basic_ostream_> *) + FUN_1001db00(uVar3); + std::basic_ostream_>::operator<< + (pbVar4,p_Var13); +LAB_1010bb4d: + piVar8 = *(int **)param_1[2]; + if (piVar8 != (int *)param_1[2]) { + do { + piVar10 = *(int **)piVar8[5]; + if (piVar10 != (int *)piVar8[5]) { + do { + piVar9 = *(int **)piVar10[5]; + if (piVar9 != (int *)piVar10[5]) { + do { + if (param_2 == '\0') { + iVar5 = (**(code **)(*param_4 + 0xc))(param_4,&local_224,4,0); + if (iVar5 < 0) { + /* WARNING: Subroutine does not return */ + FUN_1005bf30(iVar5,0, + "E:\\BldSrc\\6\\s\\sharedcomponents\\internal\\MagellanPublic\\Includes\\BaseRuntimeComponentServer\\LoadSave.h" + ,0x12); + } + local_18[0] = -(ushort)((local_224 & 0x80000000) != 0); + local_10 = local_224 >> 0x14 & 0xf; + piVar7 = (int *)piVar9[7]; + local_14 = local_224 >> 0x18 & 0xf; + local_c = (undefined2)local_224; + if (piVar7 != (int *)0x0) { + psVar6 = local_18; + goto LAB_1010bbf0; + } + } + else { + piVar7 = (int *)piVar9[7]; + psVar6 = param_3; + if (piVar7 != (int *)0x0) { +LAB_1010bbf0: + (**(code **)(*piVar7 + 0x14))(0,psVar6); + } + } + if (*(char *)((int)piVar9 + 0x21) == '\0') { + piVar7 = (int *)piVar9[2]; + if (*(char *)((int)piVar7 + 0x21) == '\0') { + cVar2 = *(char *)(*piVar7 + 0x21); + piVar9 = piVar7; + piVar7 = (int *)*piVar7; + while (cVar2 == '\0') { + cVar2 = *(char *)(*piVar7 + 0x21); + piVar9 = piVar7; + piVar7 = (int *)*piVar7; + } + } + else { + cVar2 = *(char *)(piVar9[1] + 0x21); + piVar1 = (int *)piVar9[1]; + piVar7 = piVar9; + while ((piVar9 = piVar1, cVar2 == '\0' && (piVar7 == (int *)piVar9[2]))) { + cVar2 = *(char *)(piVar9[1] + 0x21); + piVar1 = (int *)piVar9[1]; + piVar7 = piVar9; + } + } + } + } while (piVar9 != (int *)piVar10[5]); + } + if (*(char *)((int)piVar10 + 0x21) == '\0') { + piVar9 = (int *)piVar10[2]; + if (*(char *)((int)piVar9 + 0x21) == '\0') { + cVar2 = *(char *)(*piVar9 + 0x21); + piVar10 = piVar9; + piVar9 = (int *)*piVar9; + while (cVar2 == '\0') { + cVar2 = *(char *)(*piVar9 + 0x21); + piVar10 = piVar9; + piVar9 = (int *)*piVar9; + } + } + else { + cVar2 = *(char *)(piVar10[1] + 0x21); + piVar7 = (int *)piVar10[1]; + piVar9 = piVar10; + while ((piVar10 = piVar7, cVar2 == '\0' && (piVar9 == (int *)piVar10[2]))) { + cVar2 = *(char *)(piVar10[1] + 0x21); + piVar7 = (int *)piVar10[1]; + piVar9 = piVar10; + } + } + } + } while (piVar10 != (int *)piVar8[5]); + } + if (*(char *)((int)piVar8 + 0x21) == '\0') { + piVar10 = (int *)piVar8[2]; + if (*(char *)((int)piVar10 + 0x21) == '\0') { + cVar2 = *(char *)(*piVar10 + 0x21); + piVar8 = piVar10; + piVar10 = (int *)*piVar10; + while (cVar2 == '\0') { + cVar2 = *(char *)(*piVar10 + 0x21); + piVar8 = piVar10; + piVar10 = (int *)*piVar10; + } + } + else { + cVar2 = *(char *)(piVar8[1] + 0x21); + piVar9 = (int *)piVar8[1]; + piVar10 = piVar8; + while ((piVar8 = piVar9, cVar2 == '\0' && (piVar10 == (int *)piVar8[2]))) { + cVar2 = *(char *)(piVar8[1] + 0x21); + piVar9 = (int *)piVar8[1]; + piVar10 = piVar8; + } + } + } + } while (piVar8 != (int *)param_1[2]); + } + (**(code **)*param_1)(1); + __security_check_cookie(local_8 ^ (uint)&stack0xfffffffc); + return; +} + + +``` + +## FUN_1010dc80 at 1010dc80 + +Signature: `undefined FUN_1010dc80(void)` + +```c + +void __thiscall FUN_1010dc80(undefined4 *param_1,undefined4 param_2,short *param_3) + +{ + char cVar1; + undefined4 uVar2; + basic_ostream_> *pbVar3; + int iVar4; + int iVar5; + long lVar6; + _func_basic_ostream_>_ptr_basic_ostream_>_ptr + *p_Var7; + undefined4 uVar8; + wchar_t local_210 [260]; + uint local_8; + + local_8 = DAT_101d60b8 ^ (uint)&stack0xfffffffc; + cVar1 = FUN_100408d0(); + if (cVar1 != '\0') { + swprintf_s(local_210,0x104,L"", + (int)(short)*(undefined4 *)param_3,*(undefined4 *)(param_3 + 2), + *(undefined4 *)(param_3 + 4),(int)param_3[6]); + lVar6 = param_1[9]; + p_Var7 = endl_exref; + uVar2 = FUN_1001a0e0(*(undefined4 *)(DAT_101d6474 + 0x38), + L"RemotePlatformResolver::OperationComplete - mxStatus ",local_210, + L" requesterEngine ",param_1 + 6,L" requesterData "); + uVar2 = FUN_1001a0e0(uVar2); + uVar2 = FUN_1001a0e0(uVar2); + uVar2 = FUN_1001db00(uVar2); + pbVar3 = (basic_ostream_> *) + FUN_1001a0e0(uVar2); + pbVar3 = std::basic_ostream_>::operator<< + (pbVar3,lVar6); + std::basic_ostream_>::operator<< + (pbVar3,p_Var7); + } + if (((*param_3 != 0) || (*(int *)(param_3 + 2) != 3)) || ((param_3[6] != 2 && (param_3[6] != 1)))) + goto LAB_1010de0d; + if ((int)param_1[0x11] < 4) { + param_1[0x11] = param_1[0x11] + 1; + *(int *)(param_1[10] + 0x404) = *(int *)(param_1[10] + 0x404) + 1; + FUN_1010ad00(0,param_1 + 6,0xe,param_1 + 0xc,0,0,param_1,0,0,0,0); + __security_check_cookie(local_8 ^ (uint)&stack0xfffffffc); + return; + } + iVar4 = FUN_10022ff0(); + if (*(int *)(iVar4 + 0xac) == 0) { + iVar5 = FUN_1002f080(); + if (iVar5 != 0) goto LAB_1010ddd5; + uVar2 = 0; + } + else { +LAB_1010ddd5: + uVar2 = *(undefined4 *)(iVar4 + 0xac); + } + uVar8 = 0; + FUN_10022ff0(uVar2,0); + cVar1 = FUN_10022ba0(uVar2,uVar8); + if (cVar1 != '\0') { + uVar2 = FUN_10022ff0(L"RemotePlatformResolver::OperationComplete - Send retry count for MxResolveOffPlatformResults message to requesterEngine g %d p %d e %d" + ,param_1[6],param_1[7],param_1[8]); + FUN_10022cb0(uVar2); + } +LAB_1010de0d: + if (param_1 != (undefined4 *)0x0) { + (**(code **)*param_1)(1); + } + __security_check_cookie(local_8 ^ (uint)&stack0xfffffffc); + return; +} + + +``` + +## FUN_1010bd10 at 1010bd10 + +Signature: `undefined FUN_1010bd10(void)` + +```c + +void FUN_1010bd10(int *param_1,long param_2,int param_3,BSTR param_4,int param_5,long param_6, + void *param_7,int param_8,int param_9) + +{ + int iVar1; + char cVar2; + undefined4 uVar3; + basic_ostream_> *pbVar4; + int *piVar5; + int iVar6; + undefined4 uVar7; + int iVar8; + undefined4 *puVar9; + undefined4 *puVar10; + long lVar11; + long lVar12; + BSTR pOVar13; + void *pvVar14; + _func_basic_ostream_>_ptr_basic_ostream_>_ptr + *p_Var15; + undefined4 local_354; + undefined4 local_350; + uint local_34c; + undefined4 local_348 [8]; + int *local_328; + BSTR local_324; + undefined4 local_320; + undefined4 local_31c; + int local_314; + uint local_310; + void *local_30c; + undefined4 local_308; + undefined4 local_304; + undefined4 local_300; + BSTR local_2fc; + long local_2f8; + long local_2f4; + int local_2f0; + char local_2e9; + void *local_2e8; + BSTR local_2e4; + int local_2e0; + undefined *local_54; + int local_50; + int local_4c; + int local_48; + undefined *local_44; + int local_40; + int local_3c; + int local_38; + undefined4 local_34; + undefined4 local_30; + undefined4 local_2c; + undefined4 local_28; + undefined4 local_24; + int local_20; + undefined4 local_1c; + uint local_18; + uint local_14; + void *local_10; + undefined1 *puStack_c; + uint local_8; + + local_8 = 0xffffffff; + puStack_c = &LAB_10171dc6; + local_10 = ExceptionList; + local_14 = DAT_101d60b8 ^ (uint)&stack0xfffffffc; + ExceptionList = &local_10; + local_2f4 = param_2; + local_2e0 = param_3; + local_2f0 = param_5; + local_2f8 = param_6; + local_30c = param_7; + local_2e4 = param_4; + local_300 = 0; + local_308 = DAT_101d6504; + local_304 = DAT_101d6508; + FUN_10005170(local_14); + local_24 = (uint)local_24._2_2_ << 0x10; + local_20 = 0; + local_1c = 0; + local_18 = 0; + local_2e9 = '\0'; + local_2fc = (BSTR)0x0; + local_8 = 1; + local_320 = 0; + local_3c = DAT_101a21cc; + local_4c = DAT_101a21cc; + local_30 = 0; + local_2c = 0; + local_28 = 0; + local_44 = PTR_101a21c4; + local_40 = DAT_101a21c8; + local_38 = DAT_101a21d0; + local_54 = PTR_101a21c4; + local_50 = DAT_101a21c8; + local_48 = DAT_101a21d0; + cVar2 = FUN_100408d0(); + if (cVar2 != '\0') { + lVar12 = local_2f4; + lVar11 = local_2f8; + p_Var15 = endl_exref; + uVar3 = FUN_1001db60(*(undefined4 *)(DAT_101d6474 + 0x18),local_2e8,&DAT_1017b6e4, + L"GetResponse ",local_2e0,L" responseCode ",local_2f4,L" correlationId "); + uVar3 = FUN_1001a0e0(uVar3); + uVar3 = FUN_1001a0e0(uVar3); + uVar3 = FUN_100a68d0(uVar3); + pbVar4 = (basic_ostream_> *) + FUN_1001a0e0(uVar3); + pbVar4 = std::basic_ostream_>::operator<< + (pbVar4,lVar12); + pbVar4 = (basic_ostream_> *) + FUN_1001a0e0(pbVar4); + pbVar4 = std::basic_ostream_>::operator<< + (pbVar4,lVar11); + std::basic_ostream_>::operator<< + (pbVar4,p_Var15); + } + pvVar14 = local_2e8; + if (local_2f4 == 0) { + if (((local_2e0 == 0) || (local_2e0 == 0x34)) || + (cVar2 = FUN_100029c0(local_2e0), cVar2 != '\0')) { + FUN_10100ce0(&local_24,param_1); + (**(code **)(*local_328 + 0x14))(local_328,param_1); + iVar8 = local_2e0; + if ((local_2e0 == 0) || (local_2e0 == 0x34)) { + iVar6 = (**(code **)(*param_1 + 0xc))(param_1,&local_300,2,0); + if (iVar6 < 0) { + /* WARNING: Subroutine does not return */ + FUN_1005bf30(iVar6,0, + "E:\\BldSrc\\6\\s\\sharedcomponents\\internal\\MagellanPublic\\Includes\\BaseRuntimeComponentServer\\LoadSave.h" + ,0x12); + } + if (iVar8 == 0x34) { + cVar2 = FUN_100408d0(); + if (cVar2 != '\0') { + uVar3 = FUN_10004010(local_308,local_304); + uVar7 = FUN_100040a0(local_300); + lVar11 = 0; + lVar12 = local_2f8; + p_Var15 = endl_exref; + uVar3 = FUN_1001db60(*(undefined4 *)(DAT_101d6474 + 0x18),local_2e8,&DAT_1017b6e4, + L"GetResponse ",0x34,L" responseCode ",0,L" correlationId ", + local_2f8,L" quality ",uVar7,L" timestamp ",uVar3); + uVar3 = FUN_1001a0e0(uVar3); + uVar3 = FUN_1001a0e0(uVar3); + uVar3 = FUN_100a68d0(uVar3); + pbVar4 = (basic_ostream_> *) + FUN_1001a0e0(uVar3); + pbVar4 = std::basic_ostream_>:: + operator<<(pbVar4,lVar11); + pbVar4 = (basic_ostream_> *) + FUN_1001a0e0(pbVar4); + pbVar4 = std::basic_ostream_>:: + operator<<(pbVar4,lVar12); + uVar3 = FUN_1001a0e0(pbVar4); + uVar3 = FUN_1001a0e0(uVar3); + uVar3 = FUN_1001a0e0(uVar3); + pbVar4 = (basic_ostream_> *) + FUN_1001a0e0(uVar3); + std::basic_ostream_>::operator<< + (pbVar4,p_Var15); + } + iVar8 = (**(code **)(*param_1 + 0xc))(param_1,&local_308,8,0); + goto LAB_1010c44d; + } + } + goto LAB_1010cc29; + } + if (local_2e0 != 5) { + if ((local_2e0 == 0x1f) || (local_2e0 == 0x2f)) { + FUN_10100ce0(&local_24,param_1); + FUN_10100bc0(&local_30,param_1); + iVar8 = (**(code **)(*param_1 + 0xc))(param_1,&local_320,4,0); + if ((iVar8 < 0) || + ((iVar8 = (**(code **)(*param_1 + 0xc))(param_1,&local_44,0x10,0), iVar8 < 0 || + (iVar8 = (**(code **)(*param_1 + 0xc))(param_1,&local_54,0x10,0), iVar8 < 0)))) + goto LAB_1010c455; + puVar9 = (undefined4 *)FUN_100f6dc0(); + local_354 = local_30; + local_350 = local_2c; + local_34c = local_28; + puVar10 = local_348; + for (iVar8 = 8; pvVar14 = local_2e8, iVar8 != 0; iVar8 = iVar8 + -1) { + *puVar10 = *puVar9; + puVar9 = puVar9 + 1; + puVar10 = puVar10 + 1; + } + uVar7 = 0; + uVar3 = FUN_100fe160(&local_354); + FUN_100fdde0(&local_314,uVar3,uVar7); + if (local_314 == *(int *)((int)pvVar14 + 0x6e4)) { + local_314 = FUN_1006b760(0x8007000e); + } + *(undefined **)(local_314 + 0x18) = local_44; + *(int *)(local_314 + 0x1c) = local_40; + *(int *)(local_314 + 0x20) = local_3c; + *(int *)(local_314 + 0x24) = local_38; + *(undefined **)(local_314 + 0x28) = local_54; + *(int *)(local_314 + 0x2c) = local_50; + *(int *)(local_314 + 0x30) = local_4c; + *(int *)(local_314 + 0x34) = local_48; + param_4 = local_2e4; +LAB_1010cc29: + if ((((((local_2e0 == 1) || (local_2e0 == 4)) || (local_2e0 == 0x26)) || + ((local_2e0 == 0x28 || (local_2e0 == 0x3d)))) || + ((local_2e0 == 0x3f || ((local_2e0 == 0x35 || (local_2e0 == 0x38)))))) && + (7999 < (short)local_18)) { + SysFreeString(local_2fc); + local_2fc = (BSTR)0x0; + SysFreeString((BSTR)0x0); + local_2fc = (BSTR)0x0; + FUN_10102c70(&local_2e4,param_1); + pOVar13 = local_2e4; + if (local_2e4 != (BSTR)0x0) { + SysFreeString((BSTR)0x0); + local_2fc = pOVar13; + } + } + goto LAB_1010ccab; + } + if ((local_2e0 == 0x20) || (local_2e0 == 0x30)) { + FUN_10100ce0(&local_24,param_1); + FUN_10100bc0(&local_30,param_1); + FUN_1005e580(&local_320,param_1); + pvVar14 = local_2e8; + FUN_100fd9c0(&local_2e4,&local_30); + if (local_2e4 == *(BSTR *)((int)pvVar14 + 0x6e4)) { + local_44 = PTR_101a21c4; + local_40 = DAT_101a21c8; + local_3c = DAT_101a21cc; + local_38 = DAT_101a21d0; + local_54 = PTR_101a21c4; + local_50 = DAT_101a21c8; + local_4c = DAT_101a21cc; + local_48 = DAT_101a21d0; + } + else { + local_44 = *(undefined **)(local_2e4 + 0xc); + local_40 = *(int *)(local_2e4 + 0xe); + local_3c = *(int *)(local_2e4 + 0x10); + local_38 = *(int *)(local_2e4 + 0x12); + local_54 = *(undefined **)(local_2e4 + 0x14); + local_50 = *(int *)(local_2e4 + 0x16); + local_4c = *(int *)(local_2e4 + 0x18); + local_48 = *(int *)(local_2e4 + 0x1a); + } + goto LAB_1010cc29; + } + if ((local_2e0 == 0xe) && (param_4 != (BSTR)0x0)) { + local_24 = CONCAT22(local_24._2_2_,0xffff); + goto LAB_1010c0f5; + } + cVar2 = FUN_100f6f20(local_2e0); + if (cVar2 == '\0') goto LAB_1010cc29; + FUN_10100ce0(&local_24,param_1); + cVar2 = FUN_100f6fe0(local_2e0); + if ((((cVar2 != '\0') && ((short)local_24 == 0)) && (local_20 == 4)) && + ((short)local_18 == 0x1f42)) { + local_2e9 = '\x01'; + } + (**(code **)(*local_328 + 0x14))(local_328,param_1); + if (7999 < (short)local_18) { + SysFreeString((BSTR)0x0); + local_2fc = (BSTR)0x0; + FUN_10102ce0(&local_2fc,param_1); + } + FUN_100860c0(local_2f0,local_2f0,local_328,local_300,local_308,local_304,&local_24,local_2fc,1 + ,local_2f8); + SysFreeString(local_2fc); + local_2fc = (BSTR)0x0; + if (local_2e0 != 0x17) { + FUN_10100ce0(&local_24,param_1); + (**(code **)(*local_328 + 0x14))(local_328,param_1); + if (((local_2e0 == 0x18) || (local_2e0 == 0x39)) && + (FUN_1005e550(&local_300,param_1), local_2e0 == 0x39)) { + cVar2 = FUN_100408d0(); + if (cVar2 != '\0') { + uVar3 = FUN_10004010(local_308,local_304); + uVar7 = FUN_100040a0(local_300); + lVar11 = 0; + lVar12 = local_2f8; + p_Var15 = endl_exref; + uVar3 = FUN_1001db60(*(undefined4 *)(DAT_101d6474 + 0x18),local_2e8,&DAT_1017b6e4, + L"GetResponse ",0x39,L" responseCode ",0,L" correlationId ", + local_2f8,L" quality ",uVar7,L" timestamp ",uVar3); + uVar3 = FUN_1001a0e0(uVar3); + uVar3 = FUN_1001a0e0(uVar3); + uVar3 = FUN_100a68d0(uVar3); + pbVar4 = (basic_ostream_> *) + FUN_1001a0e0(uVar3); + pbVar4 = std::basic_ostream_>:: + operator<<(pbVar4,lVar11); + pbVar4 = (basic_ostream_> *) + FUN_1001a0e0(pbVar4); + pbVar4 = std::basic_ostream_>:: + operator<<(pbVar4,lVar12); + uVar3 = FUN_1001a0e0(pbVar4); + uVar3 = FUN_1001a0e0(uVar3); + uVar3 = FUN_1001a0e0(uVar3); + pbVar4 = (basic_ostream_> *) + FUN_1001a0e0(uVar3); + std::basic_ostream_>::operator<< + (pbVar4,p_Var15); + } + FUN_101006e0(&local_308,param_1); + goto LAB_1010ccf0; + } + if ((((local_2e0 == 0x19) || (local_2e0 == 0x1a)) || + ((local_2e0 == 0x2a || (local_2e0 == 0x2b)))) && (7999 < (short)local_18)) { + SysFreeString((BSTR)0x0); + local_2fc = (BSTR)0x0; + FUN_10102ce0(&local_2fc,param_1); + } + goto LAB_1010cc29; + } + local_2e4 = (BSTR)0x0; + FUN_10100e20(&local_2e4); + FUN_10100ce0(&local_24,param_1); + FUN_10100bc0(&local_30,param_1); + FUN_1005e580(&local_320,param_1); + cVar2 = FUN_100408d0(); + pOVar13 = local_2e4; + if (cVar2 != '\0') { + p_Var15 = endl_exref; + uVar3 = FUN_1001a0e0(*(undefined4 *)(DAT_101d6474 + 0x18), + L" - Nested subscribe response type is ",local_2e4,L" from ",&local_30 + ); + uVar3 = FUN_100a68d0(uVar3); + uVar3 = FUN_1001a0e0(uVar3); + pbVar4 = (basic_ostream_> *) + FUN_1001db00(uVar3); + std::basic_ostream_>::operator<< + (pbVar4,p_Var15); + } + pvVar14 = local_2e8; + if ((pOVar13 == (BSTR)0x1f) || (pOVar13 == (BSTR)0x2f)) { + FUN_1008cf70(&local_44,param_1); + FUN_1008cf70(&local_54,param_1); + puVar9 = (undefined4 *)FUN_100f6dc0(); + local_354 = local_30; + local_350 = local_2c; + local_34c = local_28; + puVar10 = local_348; + for (iVar8 = 8; pvVar14 = local_2e8, iVar8 != 0; iVar8 = iVar8 + -1) { + *puVar10 = *puVar9; + puVar9 = puVar9 + 1; + puVar10 = puVar10 + 1; + } + uVar7 = 0; + uVar3 = FUN_100fe160(&local_354); + FUN_100fdde0(&local_314,uVar3,uVar7); + if (local_314 == *(int *)((int)pvVar14 + 0x6e4)) { + local_314 = FUN_1006b760(0x8007000e); + } + *(undefined **)(local_314 + 0x18) = local_44; + *(int *)(local_314 + 0x1c) = local_40; + *(int *)(local_314 + 0x20) = local_3c; + *(int *)(local_314 + 0x24) = local_38; + *(undefined **)(local_314 + 0x28) = local_54; + *(int *)(local_314 + 0x2c) = local_50; + *(int *)(local_314 + 0x30) = local_4c; + *(int *)(local_314 + 0x34) = local_48; + } + else { + FUN_100fd9c0(&local_2e4,&local_30); + if (local_2e4 == *(BSTR *)((int)pvVar14 + 0x6e4)) { + local_44 = PTR_101a21c4; + local_40 = DAT_101a21c8; + local_3c = DAT_101a21cc; + local_38 = DAT_101a21d0; + local_54 = PTR_101a21c4; + local_50 = DAT_101a21c8; + local_4c = DAT_101a21cc; + local_48 = DAT_101a21d0; + } + else { + local_44 = *(undefined **)(local_2e4 + 0xc); + local_40 = *(int *)(local_2e4 + 0xe); + local_3c = *(int *)(local_2e4 + 0x10); + local_38 = *(int *)(local_2e4 + 0x12); + local_54 = *(undefined **)(local_2e4 + 0x14); + local_50 = *(int *)(local_2e4 + 0x16); + local_4c = *(int *)(local_2e4 + 0x18); + local_48 = *(int *)(local_2e4 + 0x1a); + } + } + goto LAB_1010c6ec; + } + FUN_10100ce0(&local_24,param_1); + FUN_10100bc0(&local_30,param_1); + iVar8 = (**(code **)(*param_1 + 0xc))(param_1,&local_320,4,0); + if (iVar8 < 0) { + /* WARNING: Subroutine does not return */ + FUN_1005bf30(iVar8,0, + "E:\\BldSrc\\6\\s\\sharedcomponents\\internal\\MagellanPublic\\Includes\\BaseRuntimeComponentServer\\LoadSave.h" + ,0x12); + } + iVar8 = (**(code **)(*param_1 + 0xc))(param_1,&local_44,0x10,0); +LAB_1010c44d: + if (iVar8 < 0) { +LAB_1010c455: + /* WARNING: Subroutine does not return */ + FUN_1005bf30(iVar8,0, + "E:\\BldSrc\\6\\s\\sharedcomponents\\internal\\MagellanPublic\\Includes\\BaseRuntimeComponentServer\\LoadSave.h" + ,0x12); + } + goto LAB_1010ccf0; + } + switch(local_2f4) { + case 1: + *(int *)((int)local_2e8 + 0x520) = *(int *)((int)local_2e8 + 0x520) + 1; + break; + case 2: + *(int *)((int)local_2e8 + 0x524) = *(int *)((int)local_2e8 + 0x524) + 1; + break; + case 3: + *(int *)((int)local_2e8 + 0x528) = *(int *)((int)local_2e8 + 0x528) + 1; + break; + case 4: + *(int *)((int)local_2e8 + 0x52c) = *(int *)((int)local_2e8 + 0x52c) + 1; + break; + case 5: + *(int *)((int)local_2e8 + 0x530) = *(int *)((int)local_2e8 + 0x530) + 1; + break; + case 6: + case 7: + case 8: + case 9: + case 10: + case 0xb: + case 0xc: + case 0xd: + case 0xe: + case 0xf: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + break; + case 0x1a: + *(int *)((int)local_2e8 + 0x534) = *(int *)((int)local_2e8 + 0x534) + 1; + break; + default: + goto switchD_1010bed9_default; + } + switch(local_2f4) { + case 1: + case 2: + local_31c = local_31c >> 0x10; + goto LAB_1010bf66; + case 3: + local_31c = (uint)local_31c._2_2_; + local_20 = 4; + goto LAB_1010bf6b; + case 4: + local_20 = 4; + local_31c = (uint)local_31c._2_2_ << 0x10; + local_1c = 3; + break; + case 5: + local_20 = 3; + local_31c = (uint)local_31c._2_2_ << 0x10; + local_1c = 3; + break; + default: + goto switchD_1010bed9_default; + case 0x1a: + local_31c = (uint)local_31c._2_2_; +LAB_1010bf66: + local_20 = 3; +LAB_1010bf6b: + local_31c = local_31c << 0x10; + local_1c = 2; + } + local_310 = CONCAT22(local_310._2_2_,(short)local_2f4); + local_24 = local_31c; + local_18 = local_310; +switchD_1010bed9_default: + if (local_2e0 == 3) { + if ((((short)local_24 != -1) && (local_2f0 != 0)) && (*(int *)(local_2f0 + 200) != 0)) { + FUN_10067aa0(*(int *)(local_2f0 + 200),&local_24); + } + goto LAB_1010ccf0; + } + if ((local_2e0 == 0x27) || (local_2e0 == 0x37)) { + if ((((short)local_24 != -1) && + ((local_2f0 != 0 && + (FUN_100fd820(&local_2e4,¶m_8), local_2e4 != *(BSTR *)((int)pvVar14 + 0x20c))))) && + (*(int *)(local_2f0 + 200) != 0)) { + FUN_10067aa0(*(int *)(local_2f0 + 200),&local_24); + } +LAB_1010ccab: + if ((local_2e0 == 0x10) || (local_2e0 == 0x11)) { + if (local_2f4 == 0) { + local_34 = CONCAT22(local_34._2_2_,0xffff); + local_28 = local_28 & 0xffff0000; + local_20 = 0; + local_24 = local_34; + local_1c = 1; + local_18 = local_28; + } + (**(code **)(*(int *)param_4 + 0x14))(local_2f8,&local_24); + goto LAB_1010d3df; + } + if ((((local_2e0 != 0x1f) && (local_2e0 != 0x2f)) && (local_2e0 != 0x20)) && + ((local_2e0 != 0x30 && (local_2e0 != 0x17)))) goto LAB_1010ccf0; +LAB_1010c6ec: + iVar8 = local_2e0; + if (local_2f0 != 0) { + FUN_10147530(&local_24,local_30,local_2c,local_28,local_320,&local_44,&local_54,local_2e0); + iVar8 = local_2e0; + } +LAB_1010c736: + pvVar14 = local_2e8; + if (((local_2f4 == 0) && (local_2f0 != 0)) && (iVar6 = *(int *)(local_2f0 + 200), iVar6 != 0)) { + if ((param_8 != 0) || (param_9 != 0)) { + FUN_100fd820(&local_2e4,¶m_8); + if (local_2e4 != *(BSTR *)((int)pvVar14 + 0x20c)) { + FUN_10067aa0(iVar6,&local_24); + } + goto LAB_1010d28a; + } +LAB_1010d2f2: + iVar8 = local_2e0; + if (local_20 != 1) { +LAB_1010d2fe: + if ((((iVar8 == 0x27) || (iVar8 == 0x2c)) || ((iVar8 == 0x37 || (iVar8 == 0x3c)))) && + ((FUN_100fd820(&local_2e4,¶m_8), local_2e4 != *(BSTR *)((int)local_2e8 + 0x20c) && + (FUN_10036d40(&local_310,local_2e4), local_2f0 != 0)))) { + (**(code **)(*(int *)(local_2f0 + 4) + 4))(); + } + } + } + else { +LAB_1010d28a: + if ((((param_8 == 0) && (param_9 == 0)) || (local_20 == 1)) || + (((((iVar8 != 0x26 && (iVar8 != 0x28)) && + ((iVar8 != 0x3d && ((iVar8 != 0x3f && (iVar8 != 0x35)))))) && (iVar8 != 0x38)) && + (((((iVar8 != 0x2a && (iVar8 != 0x2b)) && (iVar8 != 0x3a)) && + ((iVar8 != 0x3b && (iVar8 != 0x3e)))) && (iVar8 != 0x40)))))) { + if (local_2f4 == 0) goto LAB_1010d2f2; + goto LAB_1010d2fe; + } + FUN_10052750(¶m_8); + } + pvVar14 = local_30c; + if ((local_2e9 == '\0') && (local_30c != (void *)0x0)) { + FUN_100a94e0(); + operator_delete(pvVar14); + } + } + else { + if (local_2e0 == 0xd) { + if (param_4 == (BSTR)0x0) { +LAB_1010c10e: + cVar2 = FUN_100f6f20(local_2e0); + iVar8 = local_2f0; + if ((cVar2 != '\0') && + (FUN_100860c0(local_2f0,local_2f0,local_328,local_300,local_308,local_304,&local_24,0,1, + local_2f8), local_2e0 != 0x17)) { + if ((local_2e0 == 0x18) || (local_2e0 == 0x39)) { + cVar2 = '\0'; + } + else { + cVar2 = '\x01'; + } + local_2e4 = (BSTR)CONCAT31(local_2e4._1_3_,cVar2); + if ((((local_30c == (void *)0x0) && (cVar2 == '\0')) || (local_20 != 4)) || + ((local_2e0 != 0x2a && (local_2e0 != 0x19)))) { + FUN_100860c0(iVar8,param_4,local_328,local_300,local_308,local_304,&local_24,0,local_2e4 + ,local_2f8); + if (((short)local_24 != -1) && + ((((local_2e0 == 0x1b || (local_2e0 == 0x2c)) || (local_2e0 == 0x3c)) && + ((iVar8 != 0 && (*(int *)(iVar8 + 200) != 0)))))) { + FUN_10067aa0(*(int *)(iVar8 + 200),&local_24); + } + } + else if ((local_30c == (void *)0x0) && (local_2e9 = '\x01', iVar8 != 0)) { + FUN_1003ef70(iVar8); + } + } + goto LAB_1010ccab; + } + iVar8 = *(int *)((int)local_2e8 + 1000); + local_2e4 = param_4; + piVar5 = (int *)FUN_100fd880(&local_310,&local_2e4); + if (*piVar5 == iVar8) { + cVar2 = FUN_100408d0(); + if (cVar2 != '\0') { + pvVar14 = local_2e8; + p_Var15 = endl_exref; + pbVar4 = (basic_ostream_> *) + FUN_1001a0e0(*(undefined4 *)(DAT_101d6474 + 0x38), + L"AccessManager::ProcessNmxResponse - failed to find local platform resolver " + ); + pbVar4 = std::basic_ostream_>:: + operator<<(pbVar4,pvVar14); + std::basic_ostream_>::operator<< + (pbVar4,p_Var15); + } + } + else { + cVar2 = FUN_100408d0(); + if (cVar2 != '\0') { + pOVar13 = param_4; + p_Var15 = endl_exref; + pbVar4 = (basic_ostream_> *) + FUN_1001a0e0(*(undefined4 *)(DAT_101d6474 + 0x38), + L"AccessManager::ProcessNmxResponse - calling local platform resolver " + ,param_4,L" callback "); + pbVar4 = std::basic_ostream_>:: + operator<<(pbVar4,pOVar13); + pbVar4 = (basic_ostream_> *) + FUN_1001a0e0(pbVar4); + std::basic_ostream_>::operator<< + (pbVar4,p_Var15); + } + (**(code **)(*(int *)param_4 + 8))(0,0,0xffffffff,DAT_101d6504,DAT_101d6508,&local_24,0); + } + } + else { + if ((local_2e0 != 0xe) || (param_4 == (BSTR)0x0)) goto LAB_1010c10e; +LAB_1010c0f5: + (**(code **)(*(int *)param_4 + 0x14))(local_2f8,&local_24); + } +LAB_1010ccf0: + iVar8 = local_2e0; + cVar2 = FUN_100f6ef0(local_2e0); + if ((cVar2 == '\0') && (cVar2 = FUN_100f6f80(iVar8), cVar2 == '\0')) { + if (iVar8 == 9) { + if (param_4 != (BSTR)0x0) { + if (local_2f4 == 0) { + FUN_10100ce0(&local_24,param_1); + (**(code **)(*(int *)param_4 + 0xc))((short)local_24 == -1,&local_24); + } + else if (local_2f4 == 0x1a) { + (**(code **)(*(int *)param_4 + 0xc))(1,&local_24); + } + else { + (**(code **)(*(int *)param_4 + 0xc))(0,&local_24); + } + } + } + else if (iVar8 == 8) { + if (param_4 != (BSTR)0x0) { + if (local_2f4 == 0) { + local_34 = CONCAT22(local_34._2_2_,0xffff); + local_28 = local_28 & 0xffff0000; + local_20 = 0; + local_24 = local_34; + local_1c = 1; + local_18 = local_28; + } + (**(code **)(*(int *)param_4 + 0x14))(local_2f8,&local_24); + } + } + else if (iVar8 == 0x24) { + if (param_4 != (BSTR)0x0) { + if (local_2f4 == 0) { + local_34 = CONCAT22(local_34._2_2_,0xffff); + local_28 = (uint)local_28._2_2_ << 0x10; + local_20 = 0; + local_24 = local_34; + local_1c = 1; + local_18 = local_28; + } + (**(code **)(*(int *)param_4 + 0x14))(local_2f8,&local_24); + } + } + else if (iVar8 == 0x14) { + if (param_4 != (BSTR)0x0) { + if (local_2f4 == 0x1a) { + (**(code **)(*(int *)param_4 + 0xc))(1); + } + else { + (**(code **)(*(int *)param_4 + 0xc))(local_2f4 == 0,&local_24); + } + } + } + else if (((iVar8 == 0x1d) || (iVar8 == 0x1e)) && (param_4 != (BSTR)0x0)) { + iVar6 = *(int *)param_4; + if (local_2f4 == 0x1a) { + (**(code **)(iVar6 + 0xc))(1); + } + else { +LAB_1010cec6: + (**(code **)(iVar6 + 0xc))(local_2f4 == 0,&local_24); + } + } + else if (((iVar8 == 0x32) || (iVar8 == 0x33)) && (param_4 != (BSTR)0x0)) { + iVar6 = *(int *)param_4; + if (local_2f4 != 0x1a) goto LAB_1010cec6; + (**(code **)(iVar6 + 0xc))(1,&local_24); + } + else if (iVar8 == 0x25) { + if (param_4 != (BSTR)0x0) { + if (local_2f4 == 0) { + local_34 = CONCAT22(local_34._2_2_,0xffff); + local_28 = (uint)local_28._2_2_ << 0x10; + local_20 = 0; + local_24 = local_34; + local_1c = 1; + local_18 = local_28; + } + (**(code **)(*(int *)param_4 + 0x14))(local_2f8,&local_24); + } + } + else if ((iVar8 == 0x2d) || (iVar8 == 0x2e)) { + if (local_2f4 != 0) { + puVar9 = &local_24; + } + else { + puVar9 = &local_34; + } + FUN_1010b990(local_2f4 != 0,puVar9,param_1); + } +LAB_1010cf4e: + if (local_2e9 == '\0') { +LAB_1010cf5b: + iVar1 = local_20; + iVar6 = local_2f0; + if (((local_20 == 3) || (local_20 == 4)) && (local_2f0 != 0)) { + cVar2 = FUN_100029c0(iVar8); + if (((cVar2 == '\0') && (cVar2 = FUN_100f6fe0(iVar8), cVar2 == '\0')) || + ((*(char *)(iVar6 + 0x1bd) == '\0' || (iVar1 != 3)))) { + FUN_1013f750(&local_24); + } + else { + cVar2 = FUN_100408d0(); + if (cVar2 != '\0') { + p_Var15 = endl_exref; + pbVar4 = (basic_ostream_> *) + FUN_1001a0e0(*(undefined4 *)(DAT_101d6474 + 0x18), + L"AccessManager::ProcessNmxResponse - will SKIP referenceWentBad call" + ); + std::basic_ostream_>:: + operator<<(pbVar4,p_Var15); + } + } + } + } + goto LAB_1010c736; + } + if (local_2e9 != '\0') { + if (local_30c != (void *)0x0) { + FUN_10040fe0(&local_30c); + goto LAB_1010cf4e; + } + cVar2 = FUN_100029c0(iVar8); + if (cVar2 == '\0') { + cVar2 = FUN_100f6fe0(iVar8); + local_2e4 = (BSTR)((uint)local_2e4 & 0xffffff00); + if (cVar2 != '\0') goto LAB_1010d04d; + } + else { +LAB_1010d04d: + local_2e4 = (BSTR)CONCAT31(local_2e4._1_3_,1); + } + local_34 = (uint)local_34._2_2_ << 0x10; + local_28 = CONCAT22(local_28._2_2_,0x1f42); + local_30 = 4; + local_2c = 1; + FUN_100860c0(local_2f0,param_4,local_328,local_300,local_308,local_304,&local_34,local_2fc, + local_2e4,local_2f8); + goto LAB_1010cf4e; + } + if (((local_30c == (void *)0x0) || (local_20 != 4)) || (7999 < (short)local_18)) { + cVar2 = FUN_100029c0(iVar8); + if ((cVar2 == '\0') && (cVar2 = FUN_100f6fe0(iVar8), cVar2 == '\0')) { + local_2e4 = (BSTR)((uint)local_2e4 & 0xffffff00); + } + else { + local_2e4 = (BSTR)CONCAT31(local_2e4._1_3_,1); + if (param_4 != (BSTR)0x0) { + (**(code **)(*(int *)param_4 + 0x18))(); + } + } + if ((iVar8 != 0x39) || (local_2f4 == 0)) { + FUN_100860c0(local_2f0,param_4,local_328,local_300,local_308,local_304,&local_24,local_2fc, + local_2e4,local_2f8); + } + goto LAB_1010cf5b; + } + if (local_2f0 == 0) goto LAB_1010c736; + if ((((param_8 != 0) || (iVar6 = local_2f0, param_9 != 0)) && + (FUN_100fd820(&local_2e4,¶m_8), iVar6 = local_2f0, + local_2e4 != *(BSTR *)((int)local_2e8 + 0x20c))) && + (((FUN_10036d40(&local_310,local_2e4), iVar6 = local_2f0, iVar8 == 0x27 || (iVar8 == 0x2c)) + || ((iVar8 == 0x37 || (iVar8 == 0x3c)))))) { + (**(code **)(*(int *)(local_2f0 + 4) + 4))(); + } + cVar2 = FUN_1009f970(); + if ((cVar2 == '\0') && (*(int *)(iVar6 + 0xa4) != 7)) { + FUN_1003ef70(iVar6); + } + else { + *(undefined1 *)((int)local_30c + 0x44) = 1; + } + FUN_10040fe0(&local_30c); + if (*(int *)(iVar6 + 200) != 0) { + local_34 = CONCAT22(local_34._2_2_,0xffff); + local_28 = local_28 & 0xffff0000; + local_30 = 1; + local_2c = 0; + FUN_10067aa0(*(int *)(iVar6 + 200),&local_34); + } + } + if (local_2f0 != 0) { + (**(code **)(*(int *)(local_2f0 + 4) + 4))(); + } +LAB_1010d3df: + local_8 = local_8 & 0xffffff00; + SysFreeString(local_2fc); + local_8 = 0xffffffff; + if (local_328 != (int *)0x0) { + (**(code **)(*local_328 + 8))(local_328); + } + SysFreeString(local_324); + ExceptionList = local_10; + __security_check_cookie(local_14 ^ (uint)&stack0xfffffffc); + return; +} + + +``` + +## FUN_1010e410 at 1010e410 + +Signature: `undefined FUN_1010e410(void)` + +```c + +void FUN_1010e410(undefined4 param_1,undefined4 param_2,undefined4 param_3,undefined4 param_4) + +{ + undefined4 local_18; + undefined4 local_14; + undefined4 local_10; + undefined4 local_c; + uint local_8; + + local_8 = DAT_101d60b8 ^ (uint)&stack0xfffffffc; + local_18 = param_1; + local_10 = param_3; + local_14 = param_2; + local_c = param_4; + FUN_1010b990(1,&local_18,0); + __security_check_cookie(local_8 ^ (uint)&stack0xfffffffc); + return; +} + + +``` + diff --git a/analysis/ghidra/exports/Lmx.dll.synthesizer-helpers-decompile.md b/analysis/ghidra/exports/Lmx.dll.synthesizer-helpers-decompile.md new file mode 100644 index 0000000..5bdd8b6 --- /dev/null +++ b/analysis/ghidra/exports/Lmx.dll.synthesizer-helpers-decompile.md @@ -0,0 +1,574 @@ +# Lmx.dll selected decompile + +## FUN_10003fc0 at 10003fc0 + +Signature: `undefined FUN_10003fc0(void)` + +```c + +wchar_t * __thiscall +FUN_10003fc0(wchar_t *param_1,short param_2,undefined4 param_3,undefined4 param_4,short param_5) + +{ + swprintf_s(param_1,0x104,L"",(int)param_2,param_3, + param_4,(int)param_5); + return param_1; +} + + +``` + +## FUN_10016fd0 at 10016fd0 + +Signature: `undefined FUN_10016fd0(void)` + +```c + +void __fastcall FUN_10016fd0(int *param_1) + +{ + byte bStack_17; + + *param_1 = (uint)bStack_17 << 8; + param_1[1] = 0; + param_1[2] = 0; + param_1[3] = 0; + param_1[4] = 0; + return; +} + + +``` + +## FUN_1008f150 at 1008f150 + +Signature: `undefined FUN_1008f150(void)` + +```c + +void __thiscall +FUN_1008f150(int param_1,int *param_2,undefined4 param_3,undefined4 param_4,undefined4 param_5, + undefined4 param_6,undefined4 param_7,undefined4 param_8,undefined4 param_9, + undefined4 param_10,undefined4 param_11) + +{ + int iVar1; + char cVar2; + undefined4 uVar3; + undefined4 uVar4; + basic_ostream_> *pbVar5; + int iVar6; + int *piVar7; + _func_basic_ostream_>_ptr_basic_ostream_>_ptr + *p_Var8; + int *local_258; + undefined4 local_254; + ulong local_250; + ushort local_24c; + ushort uStack_24a; + uchar local_248 [4]; + uchar local_244 [4]; + undefined4 local_240; + undefined4 local_23c; + undefined4 local_238; + undefined4 local_234; + undefined4 local_230; + undefined4 local_22c; + undefined4 local_228; + undefined4 local_224; + GUID local_220 [33]; + uint local_8; + + local_8 = DAT_101d60b8 ^ (uint)&stack0xfffffffc; + CoCreateGuid(local_220); + cVar2 = FUN_100408d0(); + if (cVar2 != '\0') { + uVar3 = FUN_10047fe0(local_220[0].Data1,local_220[0]._4_4_,local_220[0].Data4._0_4_, + local_220[0].Data4._4_4_); + p_Var8 = endl_exref; + uVar4 = (**(code **)(*param_2 + 8))(); + piVar7 = param_2; + pbVar5 = (basic_ostream_> *) + FUN_1001a0e0(*(undefined4 *)(DAT_101d6474 + 0x38), + L"CReferenceStringResolver::ResolveReference - reference ",param_2, + L" guid ",uVar3,L" ref ",uVar4); + pbVar5 = std::basic_ostream_>::operator<< + (pbVar5,piVar7); + uVar3 = FUN_1001a0e0(pbVar5); + uVar3 = FUN_1001a0e0(uVar3); + uVar3 = FUN_1001a0e0(uVar3); + pbVar5 = (basic_ostream_> *) + FUN_1001a0e0(uVar3); + std::basic_ostream_>::operator<< + (pbVar5,p_Var8); + } + local_250 = local_220[0].Data1; + local_24c = local_220[0].Data2; + uStack_24a = local_220[0].Data3; + local_244[0] = local_220[0].Data4[4]; + local_244[1] = local_220[0].Data4[5]; + local_244[2] = local_220[0].Data4[6]; + local_244[3] = local_220[0].Data4[7]; + local_254 = param_11; + local_240 = param_7; + local_238 = param_9; + local_248[0] = local_220[0].Data4[0]; + local_248[1] = local_220[0].Data4[1]; + local_248[2] = local_220[0].Data4[2]; + local_248[3] = local_220[0].Data4[3]; + local_234 = param_10; + local_258 = param_2; + iVar1 = *(int *)(param_1 + 0x24); + local_22c = param_4; + local_23c = param_8; + local_228 = param_5; + local_230 = param_3; + local_224 = param_6; + iVar6 = FUN_1008e910(iVar1,*(undefined4 *)(iVar1 + 4),&local_258); + if (*(int *)(param_1 + 0x28) == 0x4924923) { + /* WARNING: Subroutine does not return */ + std::_Xlength_error("list too long"); + } + *(int *)(param_1 + 0x28) = *(int *)(param_1 + 0x28) + 1; + *(int *)(iVar1 + 4) = iVar6; + **(int **)(iVar6 + 4) = iVar6; + __security_check_cookie(local_8 ^ (uint)&stack0xfffffffc); + return; +} + + +``` + +## FUN_10112f20 at 10112f20 + +Signature: `undefined FUN_10112f20(void)` + +```c + +int * __thiscall FUN_10112f20(int *param_1,int *param_2) + +{ + UINT UVar1; + BSTR pOVar2; + + if (*param_1 != *param_2) { + AtlComPtrAssign(param_1,*param_2); + } + if ((BSTR)param_1[1] != (BSTR)param_2[1]) { + SysFreeString((BSTR)param_1[1]); + pOVar2 = (BSTR)0x0; + if ((BSTR)param_2[1] != (BSTR)0x0) { + UVar1 = SysStringByteLen((BSTR)param_2[1]); + pOVar2 = SysAllocStringByteLen((LPCSTR)param_2[1],UVar1); + } + param_1[1] = (int)pOVar2; + if ((param_2[1] != 0) && (pOVar2 == (BSTR)0x0)) { + /* WARNING: Subroutine does not return */ + FUN_100013e0(0x8007000e); + } + } + if ((BSTR)param_1[2] != (BSTR)param_2[2]) { + SysFreeString((BSTR)param_1[2]); + pOVar2 = (BSTR)0x0; + if ((BSTR)param_2[2] != (BSTR)0x0) { + UVar1 = SysStringByteLen((BSTR)param_2[2]); + pOVar2 = SysAllocStringByteLen((LPCSTR)param_2[2],UVar1); + } + param_1[2] = (int)pOVar2; + if ((param_2[2] != 0) && (pOVar2 == (BSTR)0x0)) { + /* WARNING: Subroutine does not return */ + FUN_100013e0(0x8007000e); + } + } + if ((BSTR)param_1[3] != (BSTR)param_2[3]) { + SysFreeString((BSTR)param_1[3]); + pOVar2 = (BSTR)0x0; + if ((BSTR)param_2[3] != (BSTR)0x0) { + UVar1 = SysStringByteLen((BSTR)param_2[3]); + pOVar2 = SysAllocStringByteLen((LPCSTR)param_2[3],UVar1); + } + param_1[3] = (int)pOVar2; + if ((param_2[3] != 0) && (pOVar2 == (BSTR)0x0)) { + /* WARNING: Subroutine does not return */ + FUN_100013e0(0x8007000e); + } + } + if ((BSTR)param_1[4] != (BSTR)param_2[4]) { + SysFreeString((BSTR)param_1[4]); + pOVar2 = (BSTR)0x0; + if ((BSTR)param_2[4] != (BSTR)0x0) { + UVar1 = SysStringByteLen((BSTR)param_2[4]); + pOVar2 = SysAllocStringByteLen((LPCSTR)param_2[4],UVar1); + } + param_1[4] = (int)pOVar2; + if ((param_2[4] != 0) && (pOVar2 == (BSTR)0x0)) { + /* WARNING: Subroutine does not return */ + FUN_100013e0(0x8007000e); + } + } + return param_1; +} + + +``` + +## FUN_10112da0 at 10112da0 + +Signature: `undefined FUN_10112da0(void)` + +```c + +void __fastcall FUN_10112da0(undefined4 *param_1) + +{ + int *piVar1; + uint uVar2; + void *local_10; + undefined1 *puStack_c; + int local_8; + + puStack_c = &LAB_1017253f; + local_10 = ExceptionList; + uVar2 = DAT_101d60b8 ^ (uint)&stack0xfffffffc; + ExceptionList = &local_10; + *param_1 = PreboundReference::vftable; + param_1[1] = PreboundReference::vftable; + param_1[3] = PreboundReference::vftable; + local_8 = 7; + piVar1 = (int *)param_1[0x19]; + if (piVar1 != (int *)0x0) { + (**(code **)(*piVar1 + 8))(piVar1,uVar2); + } + local_8._0_1_ = 6; + SysFreeString((BSTR)param_1[0x2b]); + local_8._0_1_ = 5; + SysFreeString((BSTR)param_1[0x27]); + local_8._0_1_ = 4; + if (7 < (uint)param_1[0x21]) { + operator_delete((void *)param_1[0x1c]); + } + param_1[0x21] = 7; + param_1[0x20] = 0; + *(undefined2 *)(param_1 + 0x1c) = 0; + local_8._0_1_ = 3; + piVar1 = (int *)param_1[0x1a]; + if (piVar1 != (int *)0x0) { + (**(code **)(*piVar1 + 8))(piVar1); + } + local_8._0_1_ = 0xc; + SysFreeString((BSTR)param_1[0x18]); + local_8._0_1_ = 0xb; + SysFreeString((BSTR)param_1[0x17]); + local_8._0_1_ = 10; + SysFreeString((BSTR)param_1[0x16]); + local_8._0_1_ = 9; + SysFreeString((BSTR)param_1[0x15]); + local_8._0_1_ = 2; + piVar1 = (int *)param_1[0x14]; + if (piVar1 != (int *)0x0) { + (**(code **)(*piVar1 + 8))(piVar1); + } + local_8._0_1_ = 1; + if (7 < (uint)param_1[0x12]) { + operator_delete((void *)param_1[0xd]); + } + param_1[0x12] = 7; + param_1[0x11] = 0; + *(undefined2 *)(param_1 + 0xd) = 0; + local_8 = (uint)local_8._1_3_ << 8; + if (7 < (uint)param_1[0xb]) { + operator_delete((void *)param_1[6]); + } + param_1[0xb] = 7; + param_1[10] = 0; + *(undefined2 *)(param_1 + 6) = 0; + *param_1 = MxConnectionCallback::vftable; + ExceptionList = local_10; + return; +} + + +``` + +## FUN_101139c0 at 101139c0 + +Signature: `undefined FUN_101139c0(void)` + +```c + +undefined4 * __thiscall FUN_101139c0(undefined4 *param_1,short *param_2,undefined4 param_3) + +{ + short sVar1; + uint uVar2; + short *psVar3; + void *local_10; + undefined1 *puStack_c; + undefined4 local_8; + + puStack_c = &LAB_101726b3; + local_10 = ExceptionList; + uVar2 = DAT_101d60b8 ^ (uint)&stack0xfffffffc; + ExceptionList = &local_10; + *param_1 = MxConnectionCallback::vftable; + local_8 = 0; + param_1[1] = CReferenceToResolve::vftable; + *(undefined1 *)(param_1 + 2) = 0; + param_1[3] = RedundancyResolutionStatusCallback::vftable; + *param_1 = PreboundReference::vftable; + param_1[1] = PreboundReference::vftable; + param_1[3] = PreboundReference::vftable; + *(undefined1 *)(param_1 + 4) = 0; + param_1[5] = 0; + param_1[0xb] = 7; + param_1[10] = 0; + *(undefined2 *)(param_1 + 6) = 0; + psVar3 = param_2; + do { + sVar1 = *psVar3; + psVar3 = psVar3 + 1; + } while (sVar1 != 0); + FUN_100363d0(param_2,(int)psVar3 - (int)(param_2 + 1) >> 1); + local_8._1_3_ = (undefined3)((uint)local_8 >> 8); + param_1[0x12] = 7; + param_1[0x11] = 0; + *(undefined2 *)(param_1 + 0xd) = 0; + local_8._0_1_ = 2; + FUN_10113900(param_2); + param_1[0x19] = param_3; + param_1[0x1a] = 0; + *(undefined1 *)(param_1 + 0x1b) = 0; + param_1[0x21] = 7; + param_1[0x20] = 0; + *(undefined2 *)(param_1 + 0x1c) = 0; + param_1[0x27] = 0; + param_1[0x2a] = 0; + param_1[0x2b] = 0; + local_8 = CONCAT31(local_8._1_3_,8); + *(undefined1 *)(param_1 + 0x2c) = 0; + param_1[0x29] = 0; + if (DAT_101d8c40 == 0) { + FUN_10113070(uVar2); + } + FUN_101133d0(); + (**(code **)(*(int *)param_1[0x19] + 4))((int *)param_1[0x19]); + ExceptionList = local_10; + return param_1; +} + + +``` + +## FUN_10113b10 at 10113b10 + +Signature: `undefined FUN_10113b10(void)` + +```c + +undefined4 * __thiscall FUN_10113b10(undefined4 *param_1,int *param_2,undefined4 param_3) + +{ + undefined4 *puVar1; + short sVar2; + char cVar3; + uint uVar4; + int iVar5; + short *psVar6; + short *psVar7; + undefined2 *puVar8; + void *local_10; + undefined1 *puStack_c; + undefined1 local_8; + undefined3 uStack_7; + + puStack_c = &LAB_1017276f; + local_10 = ExceptionList; + uVar4 = DAT_101d60b8 ^ (uint)&stack0xfffffffc; + ExceptionList = &local_10; + *param_1 = MxConnectionCallback::vftable; + param_1[1] = CReferenceToResolve::vftable; + *(undefined1 *)(param_1 + 2) = 0; + param_1[3] = RedundancyResolutionStatusCallback::vftable; + *param_1 = PreboundReference::vftable; + param_1[1] = PreboundReference::vftable; + param_1[3] = PreboundReference::vftable; + *(undefined1 *)(param_1 + 4) = 0; + param_1[5] = 0; + param_1[0xb] = 7; + param_1[10] = 0; + *(undefined2 *)(param_1 + 6) = 0; + param_1[0x12] = 7; + param_1[0x11] = 0; + *(undefined2 *)(param_1 + 0xd) = 0; + uStack_7 = 0; + local_8 = 2; + param_1[0x14] = param_2; + if (param_2 != (int *)0x0) { + (**(code **)(*param_2 + 4))(param_2,uVar4); + } + param_1[0x15] = 0; + param_1[0x16] = 0; + param_1[0x17] = 0; + param_1[0x18] = 0; + param_1[0x19] = param_3; + param_1[0x1a] = 0; + *(undefined1 *)(param_1 + 0x1b) = 0; + param_1[0x21] = 7; + param_1[0x20] = 0; + *(undefined2 *)(param_1 + 0x1c) = 0; + param_1[0x27] = 0; + param_1[0x2a] = 0; + param_1[0x2b] = 0; + _local_8 = CONCAT31(uStack_7,0xe); + *(undefined1 *)(param_1 + 0x2c) = 0; + param_1[0x29] = 0; + if (DAT_101d8c40 == 0) { + FUN_10113070(); + } + if (param_2 != (int *)0x0) { + puVar1 = param_1 + 0x15; + SysFreeString((BSTR)param_1[0x15]); + *puVar1 = 0; + iVar5 = (**(code **)(*(int *)param_1[0x14] + 0x20))((int *)param_1[0x14],puVar1); + if (iVar5 < 0) { + /* WARNING: Subroutine does not return */ + FUN_1005bf30(iVar5,0,"E:\\BldSrc\\6\\s\\ExtInterfaces\\Lmx\\IMxReferencePtr.h",0x3f); + } + psVar7 = (short *)*puVar1; + if (psVar7 == (short *)0x0) { + psVar7 = &DAT_1017a514; + } + psVar6 = psVar7; + do { + sVar2 = *psVar6; + psVar6 = psVar6 + 1; + } while (sVar2 != 0); + FUN_100363d0(psVar7,(int)psVar6 - (int)(psVar7 + 1) >> 1); + puVar1 = param_1 + 0x15; + SysFreeString((BSTR)param_1[0x15]); + *puVar1 = 0; + iVar5 = (**(code **)(*(int *)param_1[0x14] + 0x20))((int *)param_1[0x14],puVar1); + if (iVar5 < 0) { + /* WARNING: Subroutine does not return */ + FUN_1005bf30(iVar5,0,"E:\\BldSrc\\6\\s\\ExtInterfaces\\Lmx\\IMxReferencePtr.h",0x3f); + } + puVar8 = (undefined2 *)*puVar1; + if (puVar8 == (undefined2 *)0x0) { + puVar8 = &DAT_1017a514; + } + cVar3 = FUN_10134a10(puVar8); + if (cVar3 != '\0') { + psVar6 = (short *)FUN_1005f6b0(); + psVar7 = psVar6; + do { + sVar2 = *psVar7; + psVar7 = psVar7 + 1; + } while (sVar2 != 0); + FUN_100363d0(psVar6,(int)psVar7 - (int)(psVar6 + 1) >> 1); + } + } + FUN_101133d0(); + (**(code **)(*(int *)param_1[0x19] + 4))((int *)param_1[0x19]); + ExceptionList = local_10; + return param_1; +} + + +``` + +## FUN_10114620 at 10114620 + +Signature: `undefined FUN_10114620(void)` + +```c + +void __fastcall FUN_10114620(int param_1) + +{ + char cVar1; + undefined4 uVar2; + undefined4 uVar3; + undefined1 local_18 [4]; + void *local_14; + void *local_10; + undefined1 *puStack_c; + undefined4 local_8; + + local_8 = 0xffffffff; + puStack_c = &LAB_10172866; + local_10 = ExceptionList; + ExceptionList = &local_10; + if (*(char *)(*(int *)(param_1 + 100) + 0x6dc) != '\0') { + cVar1 = FUN_1005faf0(DAT_101d60b8 ^ (uint)&stack0xfffffffc); + if (cVar1 == '\0') { + local_14 = operator_new(0x1c); + local_8 = 0; + if (local_14 == (void *)0x0) { + local_14 = (void *)0x0; + } + else { + local_14 = (void *)FUN_10060b80(); + } + local_8 = 0xffffffff; + FUN_1003ec10(*(undefined4 *)(param_1 + 0x50)); + uVar3 = 0; + uVar2 = FUN_1002c750(&local_14); + FUN_10066e70(local_18,uVar2,uVar3); + local_14 = operator_new(0x1c); + local_8 = 1; + if (local_14 == (void *)0x0) { + local_14 = (void *)0x0; + } + else { + local_14 = (void *)FUN_10060b80(); + } + local_8 = 0xffffffff; + FUN_1003ec10(*(undefined4 *)(param_1 + 0x50)); + uVar3 = 0; + uVar2 = FUN_1002c750(&local_14); + FUN_10066e70(local_18,uVar2,uVar3); + *(undefined1 *)(*(int *)(param_1 + 100) + 0x6dd) = 1; + } + } + ExceptionList = local_10; + return; +} + + +``` + +## FUN_10112cd0 at 10112cd0 + +Signature: `undefined FUN_10112cd0(void)` + +```c + +void __fastcall FUN_10112cd0(int param_1) + +{ + uint uVar1; + void *pvVar2; + void *local_10; + undefined1 *puStack_c; + undefined4 local_8; + + local_8 = 0xffffffff; + puStack_c = &LAB_1017247b; + local_10 = ExceptionList; + uVar1 = DAT_101d60b8 ^ (uint)&stack0xfffffffc; + ExceptionList = &local_10; + pvVar2 = operator_new(0x38); + local_8 = 0; + if (pvVar2 != (void *)0x0) { + FUN_1009f240(*(undefined4 *)(param_1 + 0x50),*(undefined2 *)(*(int *)(param_1 + 100) + 0x2ac), + param_1,*(int *)(param_1 + 100)); + } + local_8 = 0xffffffff; + (*(code *)**(undefined4 **)(param_1 + 4))(uVar1); + ExceptionList = local_10; + return; +} + + +``` + diff --git a/analysis/ghidra/exports/Lmx.dll.synthesizer-helpers2-decompile.md b/analysis/ghidra/exports/Lmx.dll.synthesizer-helpers2-decompile.md new file mode 100644 index 0000000..dae75bb --- /dev/null +++ b/analysis/ghidra/exports/Lmx.dll.synthesizer-helpers2-decompile.md @@ -0,0 +1,1764 @@ +# Lmx.dll selected decompile + +## FUN_10100ce0 at 10100ce0 + +Signature: `undefined FUN_10100ce0(void)` + +```c + +void FUN_10100ce0(short *param_1,int *param_2) + +{ + int iVar1; + + iVar1 = (**(code **)(*param_2 + 0xc))(param_2,¶m_2,4,0); + if (iVar1 < 0) { + /* WARNING: Subroutine does not return */ + FUN_1005bf30(iVar1,0, + "E:\\BldSrc\\6\\s\\sharedcomponents\\internal\\MagellanPublic\\Includes\\BaseRuntimeComponentServer\\LoadSave.h" + ,0x12); + } + *param_1 = -(ushort)(((uint)param_2 & 0x80000000) != 0); + *(uint *)(param_1 + 2) = (uint)param_2 >> 0x18 & 0xf; + *(uint *)(param_1 + 4) = (uint)param_2 >> 0x14 & 0xf; + param_1[6] = (short)param_2; + return; +} + + +``` + +## FUN_10100bc0 at 10100bc0 + +Signature: `undefined FUN_10100bc0(void)` + +```c + +void FUN_10100bc0(uint *param_1,int *param_2) + +{ + int *piVar1; + int iVar2; + + piVar1 = param_2; + iVar2 = (**(code **)(*param_2 + 0xc))(param_2,¶m_2,2,0); + if (iVar2 < 0) { + /* WARNING: Subroutine does not return */ + FUN_1005bf30(iVar2,0, + "E:\\BldSrc\\6\\s\\sharedcomponents\\internal\\MagellanPublic\\Includes\\BaseRuntimeComponentServer\\LoadSave.h" + ,0x12); + } + *param_1 = (uint)param_2 & 0xffff; + iVar2 = (**(code **)(*piVar1 + 0xc))(piVar1,¶m_2,2,0); + if (iVar2 < 0) { + /* WARNING: Subroutine does not return */ + FUN_1005bf30(iVar2,0, + "E:\\BldSrc\\6\\s\\sharedcomponents\\internal\\MagellanPublic\\Includes\\BaseRuntimeComponentServer\\LoadSave.h" + ,0x12); + } + param_1[1] = (uint)param_2 & 0xffff; + iVar2 = (**(code **)(*piVar1 + 0xc))(piVar1,¶m_2,2,0); + if (iVar2 < 0) { + /* WARNING: Subroutine does not return */ + FUN_1005bf30(iVar2,0, + "E:\\BldSrc\\6\\s\\sharedcomponents\\internal\\MagellanPublic\\Includes\\BaseRuntimeComponentServer\\LoadSave.h" + ,0x12); + } + param_1[2] = (uint)param_2 & 0xffff; + return; +} + + +``` + +## FUN_1005e580 at 1005e580 + +Signature: `undefined FUN_1005e580(void)` + +```c + +void FUN_1005e580(undefined4 param_1,int *param_2) + +{ + int iVar1; + + iVar1 = (**(code **)(*param_2 + 0xc))(param_2,param_1,4,0); + if (iVar1 < 0) { + /* WARNING: Subroutine does not return */ + FUN_1005bf30(iVar1,0, + "E:\\BldSrc\\6\\s\\sharedcomponents\\internal\\MagellanPublic\\Includes\\BaseRuntimeComponentServer\\LoadSave.h" + ,0x12); + } + return; +} + + +``` + +## FUN_1010ee00 at 1010ee00 + +Signature: `undefined FUN_1010ee00(void)` + +```c + +/* WARNING: Function: __alloca_probe replaced with injection: alloca_probe */ + +void __thiscall FUN_1010ee00(uint param_1,int *param_2,int *param_3,undefined4 *param_4) + +{ + char cVar1; + int iVar2; + undefined4 uVar3; + basic_ostream_> *pbVar4; + int iVar5; + undefined4 *puVar6; + uint uVar7; + int *piVar8; + int extraout_EDX; + code *pcVar9; + BSTR pOVar10; + int *piVar11; + int **ppiVar12; + _func_basic_ostream_>_ptr_basic_ostream_>_ptr + *p_Var13; + undefined4 uVar14; + undefined2 uVar16; + int *piVar15; + BSTR *ppOVar17; + undefined4 uVar18; + undefined4 uVar19; + uint uStack_2cd4; + undefined1 auStack_2cc4 [64]; + undefined4 local_2c84; + undefined4 local_2c80; + undefined4 local_2c7c; + undefined4 local_2c78; + int *local_2c74; + undefined4 local_2c70; + undefined4 uStack_2c6c; + int *local_2c68; + undefined4 local_2c64; + uint uStack_2c60; + undefined4 local_2c5c; + undefined4 local_2c58 [2]; + BSTR local_2c50; + BSTR local_2c4c; + int *local_2c48; + int *local_2c44; + undefined4 local_2c40; + int *local_2c38; + int *local_2c2c; + undefined4 local_2c28; + undefined1 *local_2c24; + int local_2c20; + uint local_2c1c; + undefined1 local_2c15; + undefined2 uStack_2c14; + byte local_2c11; + int *local_2c10; + undefined4 *local_2c0c; + BSTR local_2c08; + int *local_2c04; + uint local_2c00; + undefined1 local_2bfc [2]; + undefined4 local_2bfa; + undefined2 local_2bf6; + undefined2 uStack_2bf4; + undefined2 local_2bf2; + undefined2 uStack_2bf0; + undefined4 local_2bee; + undefined2 local_2bea; + undefined4 uStack_2be8; + undefined4 uStack_2be4; + int local_2be0; + undefined4 uStack_2bdc; + undefined4 local_2bd8; + int *local_2bd4; + undefined2 local_2bd0; + undefined2 uStack_2bce; + undefined2 local_2bcc; + ushort uStack_2bca; + undefined1 local_120 [48]; + short local_f0 [2]; + int local_ec; + uint local_e8; + short local_e4; + undefined1 local_e0 [16]; + undefined1 local_d0 [2]; + undefined4 local_ce; + undefined4 local_ca; + undefined4 local_c6; + undefined4 local_c2; + undefined2 local_be; + undefined *local_bc; + undefined4 local_b8; + undefined4 local_b4; + undefined4 local_b0; + undefined *local_ac; + undefined4 local_a8; + undefined4 local_a4; + undefined4 local_a0; + undefined1 local_9c [16]; + undefined **ppuStack_8c; + undefined1 local_88 [4]; + void *pvStack_84; + undefined **local_78; + undefined1 local_74 [4]; + void *local_70; + undefined4 local_64; + undefined4 local_60; + undefined4 local_5c; + undefined4 local_58; + undefined4 local_54; + int local_50; + int *local_4c; + int local_48; + int local_44; + int local_40; + int local_3c; + undefined4 uStack_34; + uint uStack_30; + uint uStack_2c; + int local_28; + int local_24; + uint local_20; + int local_1c; + uint local_18; + undefined1 *local_14; + void *local_10; + undefined1 *puStack_c; + int local_8; + + local_8 = 0xffffffff; + puStack_c = &LAB_10172302; + local_10 = ExceptionList; + uStack_2cd4 = DAT_101d60b8 ^ (uint)&stack0xfffffffc; + local_14 = (undefined1 *)&uStack_2cd4; + ExceptionList = &local_10; + local_2c68 = param_2; + local_2c38 = param_3; + local_2c0c = param_4; + local_2bfc[0] = 0; + local_2bfa = 0; + local_2bf6 = 0; + uStack_2bf4 = 0; + local_2bf2 = 0; + uStack_2bf0 = 0; + local_2bee = 0; + local_2bea = 0; + local_2c00 = param_1; + local_18 = uStack_2cd4; + FUN_10005170(); + local_2c64 = 0xffffffff; + local_2c84 = DAT_101d6504; + local_2c80 = DAT_101d6508; + local_d0[0] = 0; + local_ce = 0; + local_ca = 0; + local_c6 = 0; + local_c2 = 0; + local_be = 0; + local_ac = PTR_101a21c4; + local_a8 = DAT_101a21c8; + local_a4 = DAT_101a21cc; + local_a0 = DAT_101a21d0; + local_2c50 = (BSTR)0x0; + local_2c4c = (BSTR)0x0; + local_8._1_3_ = 0; + local_8._0_1_ = 2; + local_bc = PTR_101a21c4; + local_b8 = DAT_101a21c8; + local_b4 = DAT_101a21cc; + local_b0 = DAT_101a21d0; + local_2c5c = 0; + local_2c58[0] = 0; + local_2c7c = 0; + local_2c78 = 0; + iVar2 = (**(code **)(*param_2 + 0xc))(param_2,&local_2c11,1,0); + if (iVar2 < 0) { +LAB_1010ef7a: + /* WARNING: Subroutine does not return */ + FUN_1005bf30(iVar2,0, + "E:\\BldSrc\\6\\s\\sharedcomponents\\internal\\MagellanPublic\\Includes\\BaseRuntimeComponentServer\\LoadSave.h" + ,0x12); + } + local_2c10 = (int *)(uint)local_2c11; + uVar19 = 0; + uVar18 = 2; + ppOVar17 = &local_2c08; + piVar11 = param_2; + iVar2 = (**(code **)(*param_2 + 0xc))(param_2,ppOVar17,2,0); + if (iVar2 < 0) { + /* WARNING: Subroutine does not return */ + FUN_1005bf30(iVar2,0, + "E:\\BldSrc\\6\\s\\sharedcomponents\\internal\\MagellanPublic\\Includes\\BaseRuntimeComponentServer\\LoadSave.h" + ,0x12); + } + local_2c1c = (uint)local_2c08 & 0xffff; + cVar1 = FUN_100033c0(local_2c10,local_2c08,local_f0); + if (cVar1 == '\0') { + local_2c00 = (local_ec << 4 | local_e8) << 0x14 | -(uint)(local_f0[0] != 0) & 0x80000000 | + (int)local_e4; + iVar2 = (**(code **)(*param_3 + 0x10))(param_3,&local_2c00,4,0); + if (iVar2 < 0) { +LAB_1010f034: + /* WARNING: Subroutine does not return */ + FUN_1005bf30(iVar2,0, + "E:\\BldSrc\\6\\s\\sharedcomponents\\internal\\MagellanPublic\\Includes\\BaseRuntimeComponentServer\\LoadSave.h" + ,0x19); + } + if (local_e4 == 0x17) { + local_2c00 = FUN_10003390(local_2c10); + local_2c00 = local_2c00 & 0xffff; + iVar2 = (**(code **)(*param_3 + 0x10))(param_3,&local_2c00,2,0); + if (iVar2 < 0) { + /* WARNING: Subroutine does not return */ + FUN_1005bf30(iVar2,0, + "E:\\BldSrc\\6\\s\\sharedcomponents\\internal\\MagellanPublic\\Includes\\BaseRuntimeComponentServer\\LoadSave.h" + ,0x19); + } + } + pcVar9 = SysFreeString_exref; + local_8._0_1_ = 1; + SysFreeString((BSTR)0x0); + local_8 = (uint)local_8._1_3_ << 8; + SysFreeString((BSTR)0x0); + local_8 = 0xffffffff; + if (local_2c74 != (int *)0x0) { + (**(code **)(*local_2c74 + 8))(local_2c74); + } + goto LAB_10112114; + } + cVar1 = FUN_100408d0(); + if (cVar1 != '\0') { + p_Var13 = endl_exref; + uVar3 = FUN_1001db60(*(undefined4 *)(DAT_101d6474 + 0x18),param_1,L" GetRequest ",local_2c10, + L" (version ",local_2c08,L") from ",local_2c0c); + uVar3 = FUN_1001a0e0(uVar3); + uVar3 = FUN_100a68d0(uVar3); + FUN_1001a0e0(uVar3); + uVar3 = FUN_10022870(); + uVar3 = FUN_1001a0e0(uVar3); + pbVar4 = (basic_ostream_> *) + FUN_1001db00(uVar3); + std::basic_ostream_>::operator<< + (pbVar4,p_Var13); + } + iVar2 = DAT_101d8c28; + if ((int)(10000U - DAT_101d8c28) < 4) { + memset(&DAT_101d6518 + DAT_101d8c28,0xfa,10000U - DAT_101d8c28); + iVar2 = 0; + } + *(undefined2 *)(&DAT_101d6518 + iVar2) = 0x3333; + *(undefined2 *)(&DAT_101d651a + iVar2) = 0x3333; + iVar5 = iVar2 + 4; + if ((int)(10000U - iVar5) < 1) { + memset(&DAT_101d651c + iVar2,0xfa,10000U - iVar5); + iVar5 = 0; + } + (&DAT_101d6518)[iVar5] = (char)local_2c10; + DAT_101d8c28 = iVar5 + 1; + if (((local_2c10 == (int *)0x0) || (local_2c10 == (int *)0x34)) || + (cVar1 = FUN_100029c0(local_2c10), cVar1 != '\0')) { + FUN_10104e90(param_2,local_2bfc,local_2c10,&local_2c74,&local_2c64,&local_2c84,local_d0, + local_2c58,&local_ac,&local_2c50,&local_2c4c,&local_bc,&local_2c5c,&local_2c7c); + goto LAB_1011202f; + } + if (local_2c10 == (int *)0x1c) { + uVar19 = 0; + uVar18 = 4; + ppiVar12 = &local_2c04; + iVar2 = (**(code **)(*param_2 + 0xc))(param_2,ppiVar12,4,0); + if (iVar2 < 0) { + /* WARNING: Subroutine does not return */ + FUN_1005bf30(iVar2,0, + "E:\\BldSrc\\6\\s\\sharedcomponents\\internal\\MagellanPublic\\Includes\\BaseRuntimeComponentServer\\LoadSave.h" + ,0x12); + } + FUN_10100ce0(local_9c,param_2); + if (local_2c04 != (int *)0x0) { + local_2c08 = (BSTR)0x0; + local_8._0_1_ = 4; + uVar3 = FUN_10030d80(); + FUN_1014ed80(local_9c,0,uVar3); + puVar6 = (undefined4 *)FUN_10005170(); + local_8._0_1_ = 5; + (**(code **)(*local_2c04 + 8)) + (0,*puVar6,0xffffffff,DAT_101d6504,DAT_101d6508,local_9c,local_2c08); + local_8._0_1_ = 4; + FUN_10005220(); + local_8 = CONCAT31(local_8._1_3_,3); + SysFreeString(local_2c08); + } + local_8 = 2; + FUN_10112046(ppiVar12,uVar18,uVar19); + return; + } + if (local_2c10 == (int *)0x29) { + local_2c2c = (int *)0x0; + local_2c28 = (BSTR)0x0; + FUN_10100d40(&local_2c2c,param_2); + FUN_10100ce0(&local_28,param_2); + FUN_100fd820(&local_2c04,&local_2c2c); + if (local_2c04 != *(int **)(param_1 + 0x20c)) { + local_50 = local_2c04[5]; + piVar11 = (int *)local_2c04[6]; + local_48 = local_2c04[7]; + local_44 = local_2c04[8]; + local_40 = local_2c04[9]; + local_3c = local_2c04[10]; + FUN_10036d40(&local_2c24,local_2c04); + iVar2 = local_50; + local_8 = CONCAT31(local_8._1_3_,7); + if (piVar11 != (int *)0x0) { + local_2c08 = (BSTR)0x0; + local_8._0_1_ = 8; + uVar18 = FUN_10030d80(); + FUN_1014ed80(&local_28,0,uVar18); + puVar6 = (undefined4 *)FUN_10005170(); + local_8._0_1_ = 9; + uVar18 = *puVar6; + uVar3 = 0xffffffff; + uVar19 = DAT_101d6504; + (**(code **)(*piVar11 + 8)) + (0,uVar18,0xffffffff,DAT_101d6504,DAT_101d6508,&local_28,local_2c08); + local_8 = CONCAT31(local_8._1_3_,8); + FUN_10005220(); + if (((char)local_3c != '\0') && (local_50 != 0)) { + (**(code **)(*(int *)(local_50 + 4) + 4))(); + } + local_8 = CONCAT31(local_8._1_3_,7); + SysFreeString(local_2c08); + local_8 = 2; + FUN_10112046(uVar18,uVar3,uVar19); + return; + } + if (((char)local_3c != '\0') && (local_50 != 0)) { + if (*(int *)(local_50 + 200) != 0) { + FUN_10067aa0(*(int *)(local_50 + 200),&local_28); + } + (**(code **)(*(int *)(iVar2 + 4) + 4))(); + } + local_8 = 2; + FUN_10112046(ppOVar17,uVar18,uVar19); + return; + } + cVar1 = FUN_100408d0(); + if (cVar1 != '\0') { + p_Var13 = endl_exref; + uVar3 = FUN_1001db60(*(undefined4 *)(DAT_101d6474 + 0x18),param_1, + L" MxSetCallback2 ignored - Set-Id Key not found in pendingSetMap"); + pbVar4 = (basic_ostream_> *) + FUN_1001a0e0(uVar3); + std::basic_ostream_>::operator<< + (pbVar4,p_Var13); + FUN_10112046(ppOVar17,uVar18,uVar19); + return; + } +code_r0x10112046: + cVar1 = FUN_100029c0(local_2c10); + if (cVar1 != '\0') goto LAB_1011205d; + } + else { + if (local_2c10 == (int *)0x14) { + FUN_1005e580(&local_2c20,param_2); + FUN_1008cf70(local_88,param_2); + puVar6 = local_2c0c; + *(int *)(param_1 + 0x53c) = *(int *)(param_1 + 0x53c) + 1; + if (local_2c20 != 0) { + do { + iVar2 = (**(code **)(*param_2 + 0xc))(param_2,&local_28,0x10,0); + if (((iVar2 < 0) || + (iVar2 = (**(code **)(*param_2 + 0xc))(param_2,&local_2c08,4,0), iVar2 < 0)) || + (iVar2 = (**(code **)(*param_2 + 0xc))(param_2,&local_2c00,2,0), iVar2 < 0)) + goto LAB_1010ef7a; + FUN_10005170(); + local_8._0_1_ = 0xb; + uVar18 = 0x1010f5b4; + piVar11 = local_2c2c; + piVar15 = param_2; + (**(code **)(*local_2c2c + 0x14))(local_2c2c,param_2); + cVar1 = FUN_100408d0(); + if (cVar1 != '\0') { + uVar19 = FUN_1001db60(*(undefined4 *)(DAT_101d6474 + 0x28),param_1,&DAT_1017b6e4); + FUN_1001a0e0(uVar19); + } + cVar1 = FUN_10003340(local_2c08,local_2c2c,local_2c00,&local_28); + if (cVar1 == '\0') { + FUN_10031c20(8); + local_8._0_1_ = 0xc; + cVar1 = FUN_100408d0(); + if (cVar1 != '\0') { + uVar19 = FUN_10047fe0(local_28,local_24,local_20,local_1c); + pOVar10 = local_2c08; + p_Var13 = endl_exref; + pbVar4 = (basic_ostream_> *) + FUN_1001a0e0(*(undefined4 *)(DAT_101d6474 + 0x2c), + L"AccessManager::ProcessNmxRequest - canceling subscription cacheIndex " + ,local_2c08,L" guid ",uVar19); + pbVar4 = std::basic_ostream_>:: + operator<<(pbVar4,(long)pOVar10); + uVar19 = FUN_1001a0e0(pbVar4); + pbVar4 = (basic_ostream_> *) + FUN_1001a0e0(uVar19); + std::basic_ostream_>:: + operator<<(pbVar4,p_Var13); + } + local_2c11 = 0x12; + iVar2 = (*(code *)local_78[4])(&local_78,&local_2c11,1,0); + if (iVar2 < 0) goto LAB_1010f034; + uVar7 = FUN_10003390(0x12); + local_2c04 = (int *)(uVar7 & 0xffff); + iVar2 = (*(code *)local_78[4])(&local_78,&local_2c04,2,0); + if (((iVar2 < 0) || + (iVar2 = (*(code *)local_78[4])(&local_78,&local_2c08,4,0), iVar2 < 0)) || + (iVar2 = (*(code *)local_78[4])(&local_78,&local_28,0x10,0), iVar2 < 0)) + goto LAB_1010f034; + FUN_1010ad00(0,puVar6,0x12,&local_78,0,2,0,0,0,0,0); + local_8._0_1_ = 0xb; + local_78 = FastStream::vftable; + if (local_70 != (void *)0x0) { + free(local_70); + } + } + local_2c20 = local_2c20 + -1; + local_8._0_1_ = 2; + if (local_2c2c != (int *)0x0) { + (**(code **)(*local_2c2c + 8))(local_2c2c); + } + SysFreeString(local_2c28); + if (local_2c20 == 0) { + FUN_10112046(uVar18,piVar11,piVar15); + return; + } + } while( true ); + } + goto code_r0x10112046; + } + if ((local_2c10 == (int *)0x1e) || (local_2c10 == (int *)0x1d)) { + FUN_1005e580(&local_2c1c,param_2); + FUN_1008cf70(&local_2bd8,param_2); + piVar11 = local_2c10; + if (local_2c10 == (int *)0x1d) { + FUN_1008cf70(&local_28,param_2); + } + else { + local_28 = *(int *)(param_1 + 0x1f0); + local_24 = *(int *)(param_1 + 500); + local_20 = *(undefined4 *)(param_1 + 0x1f8); + local_1c = *(undefined4 *)(param_1 + 0x1fc); + } + *(int *)(param_1 + 0x53c) = *(int *)(param_1 + 0x53c) + 1; + FUN_10005170(); + puVar6 = local_2c0c; + local_8._0_1_ = 0xd; + local_2c24 = (undefined1 *)CONCAT31(local_2c24._1_3_,piVar11 == (int *)0x1d); + for (; local_2c1c != 0; local_2c1c = local_2c1c - 1) { + iVar2 = (**(code **)(*param_2 + 0xc))(param_2,&local_2c04,4,0); + if ((iVar2 < 0) || + (iVar2 = (**(code **)(*param_2 + 0xc))(param_2,&uStack_2c6c,2,0), iVar2 < 0)) + goto LAB_1010ef7a; + (**(code **)(*local_2c2c + 0x14))(local_2c2c,param_2); + cVar1 = FUN_100408d0(); + if (cVar1 != '\0') { + uVar18 = FUN_1001db60(*(undefined4 *)(DAT_101d6474 + 0x28),param_1,&DAT_1017b6e4); + FUN_1001a0e0(uVar18); + } + cVar1 = FUN_1004da60(local_2c04,local_2c2c,uStack_2c6c,&local_2bd8,local_2c24,&local_28); + if (cVar1 == '\0') { + FUN_10031c20(8); + local_8._0_1_ = 0xe; + cVar1 = FUN_100408d0(); + if (cVar1 != '\0') { + uVar18 = FUN_10047fe0(local_2bd8,local_2bd4,CONCAT22(uStack_2bce,local_2bd0), + CONCAT22(uStack_2bca,local_2bcc)); + piVar11 = local_2c04; + p_Var13 = endl_exref; + pbVar4 = (basic_ostream_> *) + FUN_1001a0e0(*(undefined4 *)(DAT_101d6474 + 0x2c), + L"AccessManager::ProcessNmxRequest - canceling subscription cacheIndex " + ,local_2c04,L"publisher guid ",uVar18); + pbVar4 = std::basic_ostream_>:: + operator<<(pbVar4,(long)piVar11); + uVar18 = FUN_1001a0e0(pbVar4); + pbVar4 = (basic_ostream_> *) + FUN_1001a0e0(uVar18); + std::basic_ostream_>::operator<< + (pbVar4,p_Var13); + } + local_2c11 = 0x23; + iVar2 = (*(code *)ppuStack_8c[4])(&ppuStack_8c,&local_2c11,1,0); + if (iVar2 < 0) goto LAB_1010f034; + uStack_2c60 = FUN_10003390(0x23); + uStack_2c60 = uStack_2c60 & 0xffff; + iVar2 = (*(code *)ppuStack_8c[4])(&ppuStack_8c,&uStack_2c60,2,0); + if (((iVar2 < 0) || + (iVar2 = (*(code *)ppuStack_8c[4])(&ppuStack_8c,&local_2c04,4,0), iVar2 < 0)) || + (iVar2 = (*(code *)ppuStack_8c[4])(&ppuStack_8c,&local_28,0x10,0), iVar2 < 0)) + goto LAB_1010f034; + FUN_1010ad00(0,puVar6,0x23,&ppuStack_8c,0,2,0,0,0,0,0); + local_8._0_1_ = 0xd; + ppuStack_8c = FastStream::vftable; + if (pvStack_84 != (void *)0x0) { + free(pvStack_84); + } + } + } +LAB_10112020: + local_8._0_1_ = 2; + FUN_10005220(); + } + else { + if ((local_2c10 == (int *)0x32) || (local_2c10 == (int *)0x33)) { + FUN_1005e580(&local_2c1c,param_2); + FUN_1008cf70(&local_2bd8,param_2); + piVar11 = local_2c10; + if (local_2c10 == (int *)0x32) { + FUN_1008cf70(&local_28,param_2); + } + else { + local_28 = *(int *)(param_1 + 0x1f0); + local_24 = *(int *)(param_1 + 500); + local_20 = *(undefined4 *)(param_1 + 0x1f8); + local_1c = *(undefined4 *)(param_1 + 0x1fc); + } + *(int *)(param_1 + 0x53c) = *(int *)(param_1 + 0x53c) + 1; + FUN_10005170(); + puVar6 = local_2c0c; + local_8._0_1_ = 0xf; + local_2c24 = (undefined1 *)CONCAT31(local_2c24._1_3_,piVar11 == (int *)0x32); + if (local_2c1c != 0) { + do { + local_2c00 = 0; + FUN_1005e580(&local_2c04,param_2); + if (local_2c10 == (int *)0x32) { + FUN_101004c0(&local_2c00,param_2); + } + FUN_1005e550(&uStack_2c6c,param_2); + FUN_101006e0(&local_2be0,param_2); + (**(code **)(*local_2c2c + 0x14))(local_2c2c,param_2); + cVar1 = FUN_100408d0(); + if (cVar1 != '\0') { + uVar18 = FUN_1001db60(*(undefined4 *)(DAT_101d6474 + 0x28),param_1,&DAT_1017b6e4); + FUN_1001a0e0(uVar18); + } + cVar1 = FUN_1004de70(local_2c04,local_2c00,local_2c2c,uStack_2c6c,local_2be0,uStack_2bdc + ,&local_2bd8,local_2c24,&local_28); + if (cVar1 == '\0') { + FUN_10031c20(8); + local_8 = CONCAT31(local_8._1_3_,0x10); + cVar1 = FUN_100408d0(); + if (cVar1 != '\0') { + uVar18 = FUN_10047fe0(local_2bd8,local_2bd4,CONCAT22(uStack_2bce,local_2bd0), + CONCAT22(uStack_2bca,local_2bcc)); + piVar11 = local_2c04; + p_Var13 = endl_exref; + pbVar4 = (basic_ostream_> *) + FUN_1001a0e0(*(undefined4 *)(DAT_101d6474 + 0x2c), + L"AccessManager::ProcessNmxRequest - canceling subscription cacheIndex " + ,local_2c04,L"publisher guid ",uVar18); + pbVar4 = std::basic_ostream_> + ::operator<<(pbVar4,(long)piVar11); + uVar18 = FUN_1001a0e0(pbVar4); + pbVar4 = (basic_ostream_> *) + FUN_1001a0e0(uVar18); + std::basic_ostream_>:: + operator<<(pbVar4,p_Var13); + } + FUN_10060d10(0x23,&local_4c); + uStack_2c60 = FUN_10003390(0x23); + uStack_2c60 = uStack_2c60 & 0xffff; + FUN_10063d90(&uStack_2c60,&local_4c); + FUN_1008cfd0(&local_2c04,&local_4c); + FUN_1005e4c0(&local_28,&local_4c); + FUN_1010ad00(0,puVar6,0x23,&local_4c,0,2,0,0,0,0,0); + local_8._0_1_ = 0xf; + FUN_10005840(); + } + local_2c1c = local_2c1c - 1; + } while (local_2c1c != 0); + local_2c1c = 0; + } + goto LAB_10112020; + } + if (local_2c10 == (int *)0xc) { + FUN_1005f4f0(); + local_8._0_1_ = 0x11; + piVar15 = param_2; + (**(code **)(*local_4c + 0x14))(local_4c,param_2); + FUN_10100710(&local_2c08,param_2); + iVar2 = *(int *)(param_1 + 0x168); + piVar8 = (int *)FUN_100dca80(&local_2c24,&local_2c08); + if (*piVar8 != iVar2) { + FUN_101417d0(local_4c); + local_8 = CONCAT31(local_8._1_3_,2); + FUN_10021cc0(); + FUN_10112046(piVar15,piVar11,ppOVar17); + return; + } + cVar1 = FUN_100408d0(); + if (cVar1 != '\0') { + p_Var13 = endl_exref; + pbVar4 = (basic_ostream_> *) + FUN_1001a0e0(*(undefined4 *)(DAT_101d6474 + 0x38), + L"AccessManager::ProcessNmxRequest - MxResolveOnPlatformResults failed to find reference " + ); + pbVar4 = std::basic_ostream_>:: + operator<<(pbVar4,local_2c08); + std::basic_ostream_>::operator<< + (pbVar4,p_Var13); + } + local_8 = CONCAT31(local_8._1_3_,2); + FUN_10021cc0(); + FUN_10112046(piVar15,piVar11,ppOVar17); + return; + } + if (local_2c10 == (int *)0xd) { + FUN_1005f4f0(); + local_8._0_1_ = 0x12; + piVar15 = param_2; + (**(code **)(*local_4c + 0x14))(local_4c,param_2); + FUN_1005e580(&local_2c00,param_2); + local_2c24 = operator_new(0x48); + local_8._0_1_ = 0x13; + if (local_2c24 != (undefined1 *)0x0) { + FUN_1010d9a0(local_4c,*local_2c0c,local_2c0c[1],local_2c0c[2],local_2c00,param_1); + } + local_8 = CONCAT31(local_8._1_3_,2); + FUN_10021cc0(); + FUN_10112046(piVar15,piVar11,ppOVar17); + return; + } + if (local_2c10 == (int *)0xe) { + FUN_1005f4f0(); + local_8 = CONCAT31(local_8._1_3_,0x14); + piVar15 = param_2; + (**(code **)(*local_4c + 0x14))(local_4c,param_2); + FUN_101004c0(&local_2c00,param_2); + FUN_100fd610(&local_2c04,&local_2c00); + if (local_2c04 != *(int **)(param_1 + 0x3d4)) { + FUN_1009f440(local_4c,local_2c0c[1]); + } + local_8 = CONCAT31(local_8._1_3_,2); + FUN_10021cc0(); + FUN_10112046(piVar15,piVar11,ppOVar17); + return; + } + if (local_2c10 == (int *)0x9) { + FUN_1008cf70(local_9c,param_2); + FUN_1008cf70(local_88,param_2); + iVar2 = FUN_10001050(param_1 + 0x1f0,local_9c); + if (iVar2 == 0) { + local_2bd8 = (uint)local_2bd8._2_2_ << 0x10; + local_24 = 5; + } + else { + local_2bd8 = CONCAT22(local_2bd8._2_2_,0xffff); + local_24 = 0; + } + local_20 = (uint)(iVar2 == 0); + local_2bcc = 0; + local_1c = (uint)uStack_2bca << 0x10; + local_28 = local_2bd8; + FUN_1005fd00(&local_28,param_3); + FUN_10112046(ppOVar17,uVar18,uVar19); + return; + } + if ((local_2c10 != (int *)0x5) && (local_2c10 != (int *)0x6)) { + if ((local_2c10 == (int *)0x1f) || + ((local_2c10 == (int *)0x2f || (local_2c10 == (int *)0x21)))) { + local_2c00 = 0; + FUN_1008cf70(&local_28,param_2); + *(int *)(param_1 + 0x584) = local_28; + *(int *)(param_1 + 0x588) = local_24; + *(uint *)(param_1 + 0x58c) = local_20; + *(int *)(param_1 + 0x590) = local_1c; + if ((local_2c10 == (int *)0x1f) || (local_2c10 == (int *)0x2f)) { + FUN_1005e550((short *)(param_1 + 0x594),param_2); + if (*(short *)(param_1 + 0x594) == 0) { + *(undefined2 *)(param_1 + 0x596) = 0; + *(undefined4 *)(param_1 + 0x598) = 0; + } + else { + FUN_1005e550(param_1 + 0x596,param_2); + FUN_1005e580(param_1 + 0x598,param_2); + } + } + piVar11 = local_2c10; + piVar15 = param_2; + FUN_1005d1d0(&uStack_2c14,param_2); + uVar16 = (undefined2)((uint)piVar15 >> 0x10); + FUN_1005d1d0(&local_2c00,param_2); + FUN_101004f0(&local_2bd4,param_2); + FUN_1005e580(&local_2c04,param_2); + if ((piVar11 == (int *)0x1f) || (piVar11 == (int *)0x2f)) { + local_64 = PTR_101a21c4; + local_60 = DAT_101a21c8; + local_5c = DAT_101a21cc; + local_58 = DAT_101a21d0; + FUN_10128820(&uStack_2be8,local_2c0c,local_2c04,uStack_2c14,local_2c00,&local_2bd4, + &local_28,&local_64,piVar11,param_1 + 0x594); + piVar11 = local_2c38; + FUN_1005fd00(&uStack_2be8,local_2c38); + uStack_2c = (uint)*(ushort *)(param_1 + 0x2ac); + uStack_30 = (uint)*(ushort *)(param_1 + 0x2ae); + uStack_34 = 1; + FUN_10100c50(&uStack_34,piVar11); + FUN_1008cfd0(&DAT_101d60ec,piVar11); + FUN_1005e4c0(&local_64,piVar11); + FUN_1005e4c0(&local_28,piVar11); + iVar2 = FUN_10001050(&local_64,&PTR_101a21c4); + if ((iVar2 != 0) && (cVar1 = FUN_100408d0(), cVar1 != '\0')) { + uVar18 = FUN_10003fc0(uStack_2be8,uStack_2be4,local_2be0,uStack_2bdc); + p_Var13 = endl_exref; + uVar18 = FUN_1001a0e0(*(undefined4 *)(DAT_101d6474 + 0x2c),L"MxSubscribe2 - status ", + uVar18,L" cacheIndex ",local_2c04, + L" returning NULL publisher guid"); + uVar18 = FUN_1001a0e0(uVar18); + pbVar4 = (basic_ostream_> *) + FUN_1001a0e0(uVar18); + pbVar4 = std::basic_ostream_>:: + operator<<(pbVar4,(long)local_2c04); + pbVar4 = (basic_ostream_> *) + FUN_1001a0e0(pbVar4); + std::basic_ostream_>:: + operator<<(pbVar4,p_Var13); + } + } + else { + FUN_10124850(local_2c0c,&local_28,local_2c04,uStack_2c14,local_2bd4, + CONCAT22(uStack_2bce,local_2bd0),CONCAT22(uVar16,local_2bcc)); + } + } + else if (((local_2c10 == (int *)0x20) || (local_2c10 == (int *)0x30)) || + (local_2c10 == (int *)0x22)) { + local_2c00 = 0; + local_28 = *(int *)(param_1 + 0x584); + local_24 = *(int *)(param_1 + 0x588); + local_20 = *(undefined4 *)(param_1 + 0x58c); + local_1c = *(undefined4 *)(param_1 + 0x590); + piVar11 = param_2; + FUN_1005d1d0(&uStack_2c14,param_2); + uVar16 = (undefined2)((uint)piVar11 >> 0x10); + FUN_1005d1d0(&local_2c00,param_2); + FUN_101004f0(&local_2bd4,param_2); + FUN_1005e580(&local_2c04,param_2); + if ((local_2c10 == (int *)0x20) || (local_2c10 == (int *)0x30)) { + local_64 = PTR_101a21c4; + local_60 = DAT_101a21c8; + local_5c = DAT_101a21cc; + local_58 = DAT_101a21d0; + FUN_10128820(local_88,local_2c0c,local_2c04,uStack_2c14,local_2c00,&local_2bd4,&local_28 + ,&local_64,local_2c10,param_1 + 0x594); + FUN_1005fd00(local_88,param_3); + uStack_2c = (uint)*(ushort *)(param_1 + 0x2ac); + uStack_30 = (uint)*(ushort *)(param_1 + 0x2ae); + uStack_34 = 1; + FUN_10100c50(&uStack_34,param_3); + FUN_1008cfd0(&DAT_101d60ec,param_3); + } + else { + FUN_10124850(local_2c0c,&local_28,local_2c04,uStack_2c14,local_2bd4, + CONCAT22(uStack_2bce,local_2bd0),CONCAT22(uVar16,local_2bcc)); + } + } + else { + if (local_2c10 == (int *)0x8) { + FUN_1005e580(&local_2c24,param_2); + FUN_1008cf70(local_88,param_2); + FUN_10100ce0(local_74,param_2); + FUN_1008cf70(local_120,param_2); + FUN_10112046(ppOVar17,uVar18,uVar19); + return; + } + if (local_2c10 == (int *)0x24) { + FUN_1005e580(&local_2c00,param_2); + FUN_1008cf70(local_e0,param_2); + FUN_10100ce0(&local_28,param_2); + FUN_1008cf70(local_9c,param_2); + cVar1 = FUN_100408d0(); + if (cVar1 != '\0') { + uVar3 = FUN_1001db60(*(undefined4 *)(DAT_101d6474 + 0x24),param_1,&DAT_1017b6e4); + FUN_1001a0e0(uVar3); + } + FUN_10044c00(local_2c00,local_e0,local_28,local_24,local_20,local_1c,local_9c); + FUN_10112046(ppOVar17,uVar18,uVar19); + return; + } + if (local_2c10 == (int *)0xa) { + FUN_10044df0(local_2c0c); + FUN_10112046(ppOVar17,uVar18,uVar19); + return; + } + if (local_2c10 == (int *)0xf) { + FUN_10061c60(); + FUN_10112046(ppOVar17,uVar18,uVar19); + return; + } + if (local_2c10 != (int *)0x31) { + if (local_2c10 == (int *)0x15) { + local_2c2c = (int *)0x15; + local_2c28 = (BSTR)((uint)local_2c08 & 0xffff); + FUN_100ff1c0(param_2); + if ((int)local_2c28._2_2_ - 0x45U < 0x18) { + (*(code *)(&PTR_LAB_10112134)[(int)local_2c28._2_2_ - 0x45U])(); + return; + } + } + else { + if (local_2c10 == (int *)0x10) { + FUN_1008d2a0(param_2,local_2c0c); + FUN_10112046(ppOVar17,uVar18,uVar19); + return; + } + if (local_2c10 == (int *)0x11) { + FUN_10091ed0(param_2); + FUN_10112046(ppOVar17,uVar18,uVar19); + return; + } + if (local_2c10 == (int *)0x12) { + FUN_1005e580(&local_2c24,param_2); + FUN_1008cf70(local_88,param_2); + FUN_10112046(ppOVar17,uVar18,uVar19); + return; + } + if (local_2c10 == (int *)0x23) { + FUN_1005e580(&local_2c00,param_2); + FUN_1008cf70(&local_28,param_2); + cVar1 = FUN_10126ef0(local_2c0c,local_2c00,&local_28); + if ((cVar1 == '\0') && (cVar1 = FUN_100408d0(), cVar1 != '\0')) { + uVar3 = FUN_10047fe0(local_28,local_24,local_20,local_1c); + uVar7 = local_2c00; + p_Var13 = endl_exref; + pbVar4 = (basic_ostream_> * + )FUN_1001a0e0(*(undefined4 *)(DAT_101d6474 + 0x2c), + L"AccessManager::ProcessNmxRequest - MxCancelSubscription failed to remove subscription cacheIndex " + ,local_2c00,L" subscriber guid ",uVar3); + pbVar4 = std:: + basic_ostream_>:: + operator<<(pbVar4,uVar7); + uVar3 = FUN_1001a0e0(pbVar4); + pbVar4 = (basic_ostream_> * + )FUN_1001a0e0(uVar3); + std::basic_ostream_>:: + operator<<(pbVar4,p_Var13); + FUN_10112046(ppOVar17,uVar18,uVar19); + return; + } + } + else if (local_2c10 == (int *)0x13) { + FUN_1005e580(&local_2c00,param_2); + if (local_2c00 == 0) { + FUN_10061ef0(); + FUN_10112046(ppOVar17,uVar18,uVar19); + return; + } + } + else { + if (local_2c10 != (int *)0x16) { + cVar1 = FUN_100f6f20(local_2c10); + if (cVar1 != '\0') { + FUN_10100e20(&local_2c08,param_2); + FUN_10102da0(&local_2c1c,param_2); + FUN_10104e90(param_2,local_2bfc,local_2c08,&local_2c74,&local_2c64,&local_2c84, + local_d0,local_2c58,&local_ac,&local_2c50,&local_2c4c,&local_bc, + &local_2c5c,&local_2c7c); + local_2bfc[0] = 1; + local_2bfa = CONCAT22(*(undefined2 *)(param_1 + 0x2ac), + *(undefined2 *)(param_1 + 0x2ae)); + FUN_10005170(); + local_64 = (undefined *)((uint)local_64._2_2_ << 0x10); + local_60 = 0; + local_5c = 0; + local_58 = 0; + local_2c20 = 0; + local_2c11 = 0; + local_2c15 = 0; + local_8._0_1_ = 0x1d; + uVar18 = FUN_10022400(local_2bf6,CONCAT22(local_2bf2,uStack_2bf4)); + FUN_10016560(uVar18); + local_8 = CONCAT31(local_8._1_3_,0x1e); + (**(code **)(*local_2c48 + 0x24)) + (local_2c48,&local_2bf2,local_2c74,local_2c40,&local_64); + if (7999 < (short)local_58) { + local_2c44 = local_2c48; + iVar2 = *local_2c48; + uVar18 = FUN_10030d80(); + (**(code **)(iVar2 + 0x44))(local_2c44,&local_2bf2,&local_64,uVar18); + if (local_2c20 == 0) { + FUN_1005c2c0(&DAT_1017a514); + } + } + local_5c = 5; + local_8 = CONCAT31(local_8._1_3_,0x1d); + FUN_10014390(); + local_8 = 0x1c; + FUN_10110986(); + return; + } + if (extraout_EDX == 0x25) { + FUN_1008cf70(local_e0,param_2); + FUN_1008cf70(local_9c,param_2); + cVar1 = FUN_100408d0(); + if (cVar1 != '\0') { + uVar3 = FUN_1001db60(*(undefined4 *)(DAT_101d6474 + 0x24),param_1, + &DAT_1017b6e4); + FUN_1001a0e0(uVar3); + } + FUN_1004d840(local_e0,local_9c,*local_2c0c,local_2c0c[1],local_2c0c[2]); + FUN_10112046(ppOVar17,uVar18,uVar19); + return; + } + if ((extraout_EDX == 0x2d) || (extraout_EDX == 0x2e)) { + FUN_100838b0(); + local_2c24 = (undefined1 *)(param_1 + 0x138); + *local_2c24 = 1; + local_8._0_1_ = 0x24; + FUN_1008cf70(local_74,param_2); + local_4c = local_2c10; + local_2c04 = (int *)CONCAT31(local_2c04._1_3_,local_2c10 == (int *)0x2d); + uVar18 = FUN_10081e00(); + local_8._0_1_ = 0x25; + uStack_2c = (uint)*(ushort *)(param_1 + 0x2ac); + uStack_30 = (uint)*(ushort *)(param_1 + 0x2ae); + uStack_34 = 1; + uVar18 = FUN_10082820(auStack_2cc4,&uStack_34,uVar18); + local_8._0_1_ = 0x26; + uVar19 = 0; + uVar18 = FUN_10082a00(uVar18); + FUN_10083150(&local_2be0,uVar18,uVar19); + local_2c68 = (int *)local_2be0; + local_8._0_1_ = 0x25; + FUN_10081eb0(); + local_8 = CONCAT31(local_8._1_3_,0x24); + FUN_10081b00(); + FUN_100817c0(local_2be0,param_2); + local_2c20 = **(int **)(local_2be0 + 0x1c); + FUN_10111180(); + return; + } + goto LAB_1011202f; + } + local_2bd4 = local_2c10; + local_2bd0 = 1; + uStack_2bce = 0; + local_2bcc = 0; + FUN_100ff630(param_2); + if (CONCAT22(local_2bcc,uStack_2bce) == 1) { + local_54 = 0; + local_50 = 0; + local_4c = (int *)0x0; + FUN_100ff5c0(param_2); + if (((short)local_3c == 2) || ((short)local_3c == 3)) { + FUN_10046810(local_54,local_50,0xffffffff); + FUN_10107730(local_54,local_50,local_4c); + FUN_10128680(local_54,local_50,local_4c); + FUN_10045030(&local_54); + FUN_100fd400(local_54,local_50,local_4c,local_48,local_44,local_40,local_3c); + if (0 < local_50) { + FUN_1002f150(local_50,0xffffffff); + } + FUN_100ffc90(local_50,local_48,local_44,local_40,local_3c); + } + else { + FUN_10046810(local_54,local_50,local_4c); + FUN_10107540(&local_2c24,local_54,local_50,local_4c); + FUN_10128510(local_54,local_50,local_4c); + FUN_10044df0(&local_54); + FUN_1010a5e0(local_54,local_50,local_4c,local_48,local_44,local_40,local_3c); + } + } + if (CONCAT22(local_2bcc,uStack_2bce) == 2) { + local_28 = 0; + local_24 = 0; + local_20 = 0; + FUN_100ff670(param_2); + if (0 < local_24) { + iVar2 = FUN_10022ff0(); + if ((*(int *)(iVar2 + 0xb4) == 0) && (iVar5 = FUN_1002f080(), iVar5 == 0)) { + uVar3 = 0; + } + else { + uVar3 = *(undefined4 *)(iVar2 + 0xb4); + } + uVar14 = 0; + FUN_10022ff0(uVar3,0); + cVar1 = FUN_10022ba0(uVar3,uVar14); + if (cVar1 != '\0') { + uVar3 = FUN_10022ff0(L"Received RemoteVersion for platform %d version %d", + local_24,local_1c); + FUN_10022df0(uVar3); + } + FUN_1002f150(local_24,local_1c); + FUN_10112046(ppOVar17,uVar18,uVar19); + return; + } + } + } + } + goto code_r0x10112046; + } + } + } + } +LAB_1011202f: + if ((local_2c10 != (int *)0x0) && (local_2c10 != (int *)0x34)) goto code_r0x10112046; +LAB_1011205d: + FUN_10103ba0(local_2c38,local_2bfc,local_2c10,&local_2c74,&local_2c64,&local_2c84,local_d0, + &local_ac,&local_2c50,&local_2c4c,&local_bc,local_2c5c,local_2c0c,local_2c58[0], + local_2c7c,local_2c78); + } + pcVar9 = SysFreeString_exref; + local_8._0_1_ = 1; + SysFreeString(local_2c4c); + local_8 = (uint)local_8._1_3_ << 8; + SysFreeString(local_2c50); + local_8 = 0xffffffff; + if (local_2c74 != (int *)0x0) { + (**(code **)(*local_2c74 + 8))(local_2c74); + } +LAB_10112114: + (*pcVar9)(local_2c70); + ExceptionList = local_10; + __security_check_cookie(local_18 ^ (uint)&stack0xfffffffc); + return; +} + + +``` + +## FUN_1010d4a0 at 1010d4a0 + +Signature: `undefined FUN_1010d4a0(void)` + +```c + +/* WARNING: Removing unreachable block (ram,0x1010d4dc) */ + +undefined4 __thiscall FUN_1010d4a0(int param_1,uint param_2,long *param_3) + +{ + _Container_base0 *p_Var1; + undefined4 *puVar2; + char cVar3; + int iVar4; + undefined4 uVar5; + basic_ostream_> *pbVar6; + int iVar7; + uint uVar8; + int iVar9; + undefined4 *puVar10; + long lVar11; + long lVar12; + _func_basic_ostream_>_ptr_basic_ostream_>_ptr + *p_Var13; + undefined **local_44; + LPVOID local_40; + undefined4 local_3c; + long local_38; + undefined4 *local_34; + undefined4 *local_30; + undefined4 local_2c; + int local_24; + undefined4 local_20; + long local_1c; + long local_18; + LPVOID local_14; + void *local_10; + undefined1 *puStack_c; + uint local_8; + + uVar8 = param_2; + local_8 = 0; + puStack_c = &LAB_10171e10; + local_10 = ExceptionList; + local_14 = (LPVOID)0x0; + ExceptionList = &local_10; + local_24 = param_1; + iVar4 = (**(code **)(**(int **)(param_1 + 0x270) + 0x50)) + (*(int **)(param_1 + 0x270),param_2,&local_20,&local_1c,&local_18,&local_14, + DAT_101d60b8 ^ (uint)&stack0xfffffffc); + if (iVar4 < 0) { + /* WARNING: Subroutine does not return */ + FUN_1005bf30(iVar4,0,"e:\\bldsrc\\6\\s\\src\\lmx\\CNmxAdapter.h",99); + } + if (iVar4 != 0) { + local_8 = 0xffffffff; + if (local_14 != (LPVOID)0x0) { + CoTaskMemFree(local_14); + } + ExceptionList = local_10; + return 0; + } + *param_3 = local_18; + cVar3 = FUN_100408d0(); + if (cVar3 != '\0') { + uVar8 = uVar8 & 0xff; + lVar11 = local_18; + lVar12 = local_1c; + p_Var13 = endl_exref; + uVar5 = FUN_1001db60(*(undefined4 *)(DAT_101d6474 + 0x18),param_1, + L" AccessManager::ProcessNmxResponses received message block (",local_18, + L" bytes) requestId ",local_1c,L" type "); + pbVar6 = (basic_ostream_> *) + FUN_1001a0e0(uVar5); + pbVar6 = std::basic_ostream_>::operator<< + (pbVar6,lVar11); + pbVar6 = (basic_ostream_> *) + FUN_1001a0e0(pbVar6); + pbVar6 = std::basic_ostream_>::operator<< + (pbVar6,lVar12); + pbVar6 = (basic_ostream_> *) + FUN_1001a0e0(pbVar6); + pbVar6 = std::basic_ostream_>::operator<< + (pbVar6,uVar8); + std::basic_ostream_>::operator<< + (pbVar6,p_Var13); + } + iVar4 = DAT_101d8c28; + if ((int)(10000U - DAT_101d8c28) < 4) { + memset(&DAT_101d6518 + DAT_101d8c28,0xfa,10000U - DAT_101d8c28); + iVar4 = 0; + } + *(undefined2 *)(&DAT_101d6518 + iVar4) = 0xfefe; + *(undefined2 *)(&DAT_101d651a + iVar4) = 0xfefe; + iVar7 = iVar4 + 4; + if ((int)(10000U - iVar7) < 1) { + memset(&DAT_101d651c + iVar4,0xfa,10000U - iVar7); + iVar7 = 0; + } + (&DAT_101d6518)[iVar7] = (undefined1)local_20; + iVar4 = iVar7 + 1; + if ((int)(10000U - iVar4) < 4) { + memset(&DAT_101d6519 + iVar7,0xfa,10000U - iVar4); + iVar4 = 0; + } + (&DAT_101d6518)[iVar4] = (char)((uint)local_18 >> 0x18); + (&DAT_101d6519)[iVar4] = (char)((uint)local_18 >> 0x10); + (&DAT_101d651a)[iVar4] = (char)((uint)local_18 >> 8); + (&DAT_101d651b)[iVar4] = (char)local_18; + iVar7 = iVar4 + 4; + if ((int)(10000U - iVar7) < 4) { + memset(&DAT_101d651c + iVar4,0xfa,10000U - iVar7); + iVar7 = 0; + } + (&DAT_101d6518)[iVar7] = (char)((uint)local_1c >> 0x18); + (&DAT_101d6519)[iVar7] = (char)((uint)local_1c >> 0x10); + (&DAT_101d651a)[iVar7] = (char)((uint)local_1c >> 8); + (&DAT_101d651b)[iVar7] = (char)local_1c; + DAT_101d8c28 = iVar7 + 4; + FUN_100fd6e0(¶m_2,&local_1c); + uVar8 = param_2; + if (param_2 != *(uint *)(param_1 + 0x2a0)) { + local_34 = (undefined4 *)0x0; + local_30 = (undefined4 *)0x0; + local_2c = 0; + local_8 = CONCAT31(local_8._1_3_,2); + local_3c = 0; + p_Var1 = (_Container_base0 *)(param_2 + 0x1c); + local_40 = local_14; + local_38 = local_18; + local_44 = MoreAccessFastReadOnlyStream::vftable; + if ((_Container_base0 *)&local_34 != p_Var1) { + std::_Container_base0::_Swap_all((_Container_base0 *)&local_34,p_Var1); + puVar10 = *(undefined4 **)p_Var1; + *(undefined4 **)p_Var1 = local_34; + puVar2 = *(undefined4 **)(uVar8 + 0x20); + *(undefined4 **)(uVar8 + 0x20) = local_30; + uVar5 = *(undefined4 *)(uVar8 + 0x24); + *(undefined4 *)(uVar8 + 0x24) = local_2c; + local_34 = puVar10; + local_30 = puVar2; + local_2c = uVar5; + } + FUN_10078cb0(¶m_2,uVar8); + puVar2 = local_30; + puVar10 = local_34; + iVar7 = (int)local_30 - (int)local_34; + iVar9 = iVar7 >> 5; + iVar4 = DAT_101d8c28; + if ((int)(10000U - DAT_101d8c28) < 4) { + memset(&DAT_101d6518 + DAT_101d8c28,0xfa,10000U - DAT_101d8c28); + iVar4 = 0; + } + (&DAT_101d6518)[iVar4] = (char)(iVar7 >> 0x1d); + (&DAT_101d6519)[iVar4] = (char)((uint)iVar9 >> 0x10); + (&DAT_101d651a)[iVar4] = (char)((uint)iVar9 >> 8); + (&DAT_101d651b)[iVar4] = (char)iVar9; + iVar7 = iVar4 + 4; + if (puVar10 != puVar2) { + iVar4 = iVar4 + 4; + puVar10 = puVar10 + 5; + do { + if ((int)(10000U - iVar4) < 4) { + memset(&DAT_101d6518 + iVar4,0xfa,10000U - iVar4); + iVar4 = 0; + } + *(undefined2 *)(&DAT_101d6518 + iVar4) = 0x2222; + *(undefined2 *)(&DAT_101d651a + iVar4) = 0x2222; + iVar7 = iVar4 + 4; + uVar5 = puVar10[-4]; + DAT_101d8c28 = iVar7; + if ((int)(10000U - iVar7) < 1) { + memset(&DAT_101d651c + iVar4,0xfa,10000U - iVar7); + iVar7 = 0; + } + (&DAT_101d6518)[iVar7] = (char)uVar5; + iVar4 = iVar7 + 1; + if ((int)(10000U - iVar4) < 4) { + memset(&DAT_101d6519 + iVar7,0xfa,10000U - iVar4); + iVar4 = 0; + } + (&DAT_101d6518)[iVar4] = (char)((uint)local_3c >> 0x18); + (&DAT_101d6519)[iVar4] = (char)((uint)local_3c >> 0x10); + (&DAT_101d651a)[iVar4] = (char)((uint)local_3c >> 8); + (&DAT_101d651b)[iVar4] = (char)local_3c; + DAT_101d8c28 = iVar4 + 4; + FUN_1010bd10(&local_44,local_20,puVar10[-4],puVar10[-3],puVar10[-2],puVar10[-1],*puVar10, + puVar10[1],puVar10[2]); + puVar2 = puVar10 + 3; + iVar7 = DAT_101d8c28; + iVar4 = DAT_101d8c28; + puVar10 = puVar10 + 8; + } while (puVar2 != local_30); + } + if ((int)(10000U - iVar7) < 4) { + memset(&DAT_101d6518 + iVar7,0xfa,10000U - iVar7); + iVar7 = 0; + } + (&DAT_101d6518)[iVar7] = (char)((uint)local_3c >> 0x18); + (&DAT_101d6519)[iVar7] = (char)((uint)local_3c >> 0x10); + (&DAT_101d651a)[iVar7] = (char)((uint)local_3c >> 8); + (&DAT_101d651b)[iVar7] = (char)local_3c; + DAT_101d8c28 = iVar7 + 4; + local_8 = local_8 & 0xffffff00; + if (local_34 != (undefined4 *)0x0) { + std::_Container_base0::_Orphan_all((_Container_base0 *)&local_34); + operator_delete(local_34); + } + local_34 = (undefined4 *)0x0; + local_30 = (undefined4 *)0x0; + local_2c = 0; + } + local_8 = 0xffffffff; + if (local_14 != (LPVOID)0x0) { + CoTaskMemFree(local_14); + } + ExceptionList = local_10; + return 1; +} + + +``` + +## FUN_100860c0 at 100860c0 + +Signature: `undefined FUN_100860c0(void)` + +```c + +void FUN_100860c0(void *param_1,int *param_2,undefined4 param_3,ushort param_4,undefined4 param_5, + undefined4 param_6,undefined4 param_7,undefined4 param_8,char param_9, + undefined4 param_10) + +{ + char cVar1; + basic_ostream_> *pbVar2; + void *pvVar3; + _func_basic_ostream_>_ptr_basic_ostream_>_ptr + *p_Var4; + uint uStack_458; + BSTR local_43c; + void *local_438; + uint local_434; + uint local_20; + undefined1 *local_1c; + void *local_14; + undefined1 *puStack_10; + uint local_c; + undefined4 local_8; + + local_8 = 0xfffffffe; + puStack_10 = &LAB_10150ad2; + local_14 = ExceptionList; + local_c = DAT_101d60b8 ^ 0x101bd3b8; + uStack_458 = DAT_101d60b8 ^ (uint)&stack0xfffffffc; + local_1c = (undefined1 *)&uStack_458; + ExceptionList = &local_14; + local_438 = param_1; + local_434 = (uint)param_4; + local_20 = uStack_458; + cVar1 = FUN_100408d0(); + if (cVar1 != '\0') { + pvVar3 = local_438; + p_Var4 = endl_exref; + pbVar2 = (basic_ostream_> *) + FUN_1001a0e0(*(undefined4 *)(DAT_101d6474 + 0x14), + L"AccessManager::doGetOrSetCallback - reference "); + pbVar2 = std::basic_ostream_>::operator<< + (pbVar2,pvVar3); + std::basic_ostream_>::operator<< + (pbVar2,p_Var4); + } + if (param_2 == (int *)0x0) { + cVar1 = FUN_100408d0(); + if (cVar1 != '\0') { + p_Var4 = endl_exref; + pbVar2 = (basic_ostream_> *) + FUN_1001a0e0(*(undefined4 *)(DAT_101d6474 + 0x14), + L"doGetOrSetCallback found NULL callback address"); + std::basic_ostream_>::operator<< + (pbVar2,p_Var4); + } + } + else { + local_8 = 0; + if (param_9 == '\0') { + (**(code **)(*param_2 + 4))(param_3,local_434,param_5,param_6,param_7); + } + else { + FUN_1014ed80(param_7,param_8,&local_43c); + if (local_43c == (BSTR)0x0) { + (**(code **)(*param_2 + 8))(param_10,param_3,local_434,param_5,param_6,param_7,0); + } + else { + (**(code **)(*param_2 + 8))(param_10,param_3,local_434,param_5,param_6,param_7,local_43c); + SysFreeString(local_43c); + } + } + local_8 = 0xfffffffe; + } + ExceptionList = local_14; + __security_check_cookie(local_20 ^ (uint)&stack0xfffffffc); + return; +} + + +``` + +## FUN_10067aa0 at 10067aa0 + +Signature: `undefined FUN_10067aa0(void)` + +```c + +void __thiscall FUN_10067aa0(int param_1,byte *param_2,undefined4 param_3) + +{ + int *piVar1; + uint uVar2; + int iVar3; + int *local_5c; + BSTR local_58; + undefined **local_54 [2]; + void *local_4c; + uint local_40; + undefined1 local_39; + undefined1 local_38 [4]; + uint local_34; + uint local_30; + uint local_2c; + undefined1 local_28 [2]; + undefined4 local_26; + undefined4 local_22; + undefined4 local_1e; + undefined4 local_1a; + undefined2 local_16; + uint local_14; + void *local_10; + undefined1 *puStack_c; + int local_8; + + local_8 = 0xffffffff; + puStack_c = &LAB_1015f7b0; + local_10 = ExceptionList; + uVar2 = DAT_101d60b8 ^ (uint)&stack0xfffffffc; + ExceptionList = &local_10; + local_14 = uVar2; + FUN_10005550(param_3); + local_8 = 0; + if (param_2 != (byte *)0x0) { + if (((*(ushort *)(param_2 + 4) == *(ushort *)(param_1 + 0x2ac)) && + (*(short *)(param_2 + 2) == *(short *)(param_1 + 0x2ae))) && (*param_2 == 1)) { + uVar2 = (uint)*(ushort *)(param_2 + 6); + if ((uVar2 < (uint)((*(int *)(param_1 + 0x3c4) - *(int *)(param_1 + 0x3c0)) / 0x24)) && + (*(int *)(*(int *)(param_1 + 0x3c0) + uVar2 * 0x24) != 0)) { + piVar1 = *(int **)(*(int *)(param_1 + 0x3c0) + uVar2 * 0x24); + (**(code **)(*piVar1 + 0x2c))(piVar1,param_2 + 10,local_5c,0,local_38); + } + } + else { + local_34 = (uint)*param_2; + local_30 = (uint)*(ushort *)(param_2 + 2); + local_2c = (uint)*(ushort *)(param_2 + 4); + FUN_10031c20(8); + local_8._0_1_ = 1; + local_39 = 3; + iVar3 = (*(code *)local_54[0][4])(local_54,&local_39,1,0,uVar2); + if (iVar3 < 0) { + /* WARNING: Subroutine does not return */ + FUN_1005bf30(iVar3,0, + "E:\\BldSrc\\6\\s\\sharedcomponents\\internal\\MagellanPublic\\Includes\\BaseRuntimeComponentServer\\LoadSave.h" + ,0x19); + } + local_40 = 1; + iVar3 = (*(code *)local_54[0][4])(local_54,&local_40,2,0); + if (iVar3 < 0) { + /* WARNING: Subroutine does not return */ + FUN_1005bf30(iVar3,0, + "E:\\BldSrc\\6\\s\\sharedcomponents\\internal\\MagellanPublic\\Includes\\BaseRuntimeComponentServer\\LoadSave.h" + ,0x19); + } + local_40 = (uint)*(ushort *)(param_2 + 6); + iVar3 = (*(code *)local_54[0][4])(local_54,&local_40,2,0); + if (iVar3 < 0) { + /* WARNING: Subroutine does not return */ + FUN_1005bf30(iVar3,0, + "E:\\BldSrc\\6\\s\\sharedcomponents\\internal\\MagellanPublic\\Includes\\BaseRuntimeComponentServer\\LoadSave.h" + ,0x19); + } + local_40 = (uint)*(ushort *)(param_2 + 8); + iVar3 = (*(code *)local_54[0][4])(local_54,&local_40,2,0); + if (iVar3 < 0) { + /* WARNING: Subroutine does not return */ + FUN_1005bf30(iVar3,0, + "E:\\BldSrc\\6\\s\\sharedcomponents\\internal\\MagellanPublic\\Includes\\BaseRuntimeComponentServer\\LoadSave.h" + ,0x19); + } + iVar3 = (*(code *)local_54[0][4])(local_54,param_2 + 10,10,0); + if (iVar3 < 0) { + /* WARNING: Subroutine does not return */ + FUN_1005bf30(iVar3,0, + "E:\\BldSrc\\6\\s\\sharedcomponents\\internal\\MagellanPublic\\Includes\\BaseRuntimeComponentServer\\LoadSave.h" + ,0x19); + } + iVar3 = (**(code **)(*local_5c + 0x18))(local_5c,local_54,0); + if (iVar3 < 0) { + /* WARNING: Subroutine does not return */ + FUN_1005bf30(iVar3,0,"AccessManager.cpp",0x6e4); + } + local_28[0] = 0; + local_26 = 0; + local_22 = 0; + local_1e = 0; + local_1a = 0; + local_16 = 0; + FUN_10066650(local_28,local_54); + FUN_1010ad00(0,&local_34,3,local_54,0,2,0,0,0,0,0); + local_8 = (uint)local_8._1_3_ << 8; + local_54[0] = FastStream::vftable; + if (local_4c != (void *)0x0) { + free(local_4c); + } + } + } + local_8 = 0xffffffff; + if (local_5c != (int *)0x0) { + (**(code **)(*local_5c + 8))(local_5c); + } + SysFreeString(local_58); + ExceptionList = local_10; + __security_check_cookie(local_14 ^ (uint)&stack0xfffffffc); + return; +} + + +``` + +## FUN_10100e20 at 10100e20 + +Signature: `undefined FUN_10100e20(void)` + +```c + +void FUN_10100e20(uint *param_1,int *param_2) + +{ + int iVar1; + + iVar1 = (**(code **)(*param_2 + 0xc))(param_2,(int)¶m_2 + 3,1,0); + if (iVar1 < 0) { + /* WARNING: Subroutine does not return */ + FUN_1005bf30(iVar1,0, + "E:\\BldSrc\\6\\s\\sharedcomponents\\internal\\MagellanPublic\\Includes\\BaseRuntimeComponentServer\\LoadSave.h" + ,0x12); + } + *param_1 = (uint)param_2 >> 0x18; + return; +} + + +``` + +## FUN_10101360 at 10101360 + +Signature: `undefined FUN_10101360(void)` + +```c + +void __fastcall FUN_10101360(int param_1) + +{ + undefined4 *puVar1; + char cVar2; + undefined4 uVar3; + int *piVar4; + int *piVar5; + uint uVar6; + int iVar7; + int *piVar8; + undefined2 *puVar9; + int local_220; + wchar_t local_21c [260]; + uint local_14; + void *local_10; + undefined1 *puStack_c; + undefined4 local_8; + + puStack_c = &LAB_101709ee; + local_10 = ExceptionList; + uVar6 = DAT_101d60b8 ^ (uint)&stack0xfffffffc; + ExceptionList = &local_10; + uVar3 = *(undefined4 *)(param_1 + 0x3fc); + local_14 = uVar6; + if ((DAT_101d6444 & 1) == 0) { + DAT_101d6444 = DAT_101d6444 | 1; + local_8 = 0; + FUN_10003a90(uVar6); + _atexit(FUN_101792e0); + } + local_8 = 0xffffffff; + FUN_10022d50(&DAT_101d6370,L"AccessManager::DumpState - remotePlatformResolvers.size %d",uVar3); + if ((DAT_101d6444 & 1) == 0) { + DAT_101d6444 = DAT_101d6444 | 1; + local_8 = 1; + FUN_10003a90(uVar6); + _atexit(FUN_101792e0); + local_8 = 0xffffffff; + } + FUN_10022d50(&DAT_101d6370,L" remotePlatformResolvers.sendResultsRetryCount %d", + *(undefined4 *)(param_1 + 0x404)); + piVar8 = (int *)**(int **)(param_1 + 0x3f8); + local_220 = 0; + if (piVar8 != *(int **)(param_1 + 0x3f8)) { + do { + if ((DAT_101d6444 & 1) == 0) { + DAT_101d6444 = DAT_101d6444 | 1; + local_8 = 2; + FUN_10003a90(); + _atexit(FUN_101792e0); + local_8 = 0xffffffff; + } + FUN_10022d50(&DAT_101d6370,L" remotePlatformResolvers[%d].this %x",local_220, + piVar8[3]); + iVar7 = piVar8[3]; + SysFreeString(*(BSTR *)(iVar7 + 8)); + *(undefined4 *)(iVar7 + 8) = 0; + iVar7 = (**(code **)(**(int **)(iVar7 + 4) + 0x20)) + (*(int **)(iVar7 + 4),(undefined4 *)(iVar7 + 8)); + if (iVar7 < 0) { +LAB_10101768: + /* WARNING: Subroutine does not return */ + FUN_1005bf30(iVar7,0,"E:\\BldSrc\\6\\s\\ExtInterfaces\\Lmx\\IMxReferencePtr.h",0x3f); + } + iVar7 = piVar8[3]; + puVar1 = (undefined4 *)(iVar7 + 8); + SysFreeString(*(BSTR *)(iVar7 + 8)); + *puVar1 = 0; + iVar7 = (**(code **)(**(int **)(iVar7 + 4) + 0x20))(*(int **)(iVar7 + 4),puVar1); + if (iVar7 < 0) goto LAB_10101768; + puVar9 = (undefined2 *)*puVar1; + if (puVar9 == (undefined2 *)0x0) { + puVar9 = &DAT_1017a514; + } + if ((DAT_101d6444 & 1) == 0) { + DAT_101d6444 = DAT_101d6444 | 1; + local_8 = 3; + FUN_10003a90(); + _atexit(FUN_101792e0); + local_8 = 0xffffffff; + } + FUN_10022d50(&DAT_101d6370,L" remotePlatformResolvers[%d].mxReference %s",local_220,puVar9 + ); + iVar7 = piVar8[3]; + _snwprintf_s(local_21c,0x104,0xffffffff,L"", + *(undefined4 *)(iVar7 + 0x18),*(undefined4 *)(iVar7 + 0x1c), + *(undefined4 *)(iVar7 + 0x20)); + if ((DAT_101d6444 & 1) == 0) { + DAT_101d6444 = DAT_101d6444 | 1; + local_8 = 4; + FUN_10003a90(); + _atexit(FUN_101792e0); + local_8 = 0xffffffff; + } + FUN_10022d50(&DAT_101d6370,L" remotePlatformResolvers[%d].requesterEngine %s",local_220, + local_21c); + if ((DAT_101d6444 & 1) == 0) { + DAT_101d6444 = DAT_101d6444 | 1; + local_8 = 5; + FUN_10003a90(); + _atexit(FUN_101792e0); + local_8 = 0xffffffff; + } + FUN_10022d50(&DAT_101d6370,L" remotePlatformResolvers[%d].requesterData %x",local_220, + *(undefined4 *)(piVar8[3] + 0x24)); + if ((DAT_101d6444 & 1) == 0) { + DAT_101d6444 = DAT_101d6444 | 1; + local_8 = 6; + FUN_10003a90(); + _atexit(FUN_101792e0); + local_8 = 0xffffffff; + } + FUN_10022d50(&DAT_101d6370,L" remotePlatformResolvers[%d].currentEngineId %x",local_220, + *(undefined4 *)(piVar8[3] + 0x2c)); + if ((DAT_101d6444 & 1) == 0) { + DAT_101d6444 = DAT_101d6444 | 1; + local_8 = 7; + FUN_10003a90(); + _atexit(FUN_101792e0); + local_8 = 0xffffffff; + } + FUN_10022d50(&DAT_101d6370,L" remotePlatformResolvers[%d].retryCount %d",local_220, + *(undefined4 *)(piVar8[3] + 0x44)); + if (*(char *)((int)piVar8 + 0x11) == '\0') { + piVar4 = (int *)piVar8[2]; + if (*(char *)((int)piVar4 + 0x11) == '\0') { + cVar2 = *(char *)(*piVar4 + 0x11); + piVar8 = piVar4; + piVar4 = (int *)*piVar4; + while (cVar2 == '\0') { + cVar2 = *(char *)(*piVar4 + 0x11); + piVar8 = piVar4; + piVar4 = (int *)*piVar4; + } + } + else { + cVar2 = *(char *)(piVar8[1] + 0x11); + piVar5 = (int *)piVar8[1]; + piVar4 = piVar8; + while ((piVar8 = piVar5, cVar2 == '\0' && (piVar4 == (int *)piVar8[2]))) { + cVar2 = *(char *)(piVar8[1] + 0x11); + piVar5 = (int *)piVar8[1]; + piVar4 = piVar8; + } + } + } + local_220 = local_220 + 1; + } while (piVar8 != *(int **)(param_1 + 0x3f8)); + } + if ((DAT_101d6444 & 1) == 0) { + DAT_101d6444 = DAT_101d6444 | 1; + local_8 = 8; + FUN_10003a90(); + _atexit(FUN_101792e0); + local_8 = 0xffffffff; + } + FUN_10022d50(&DAT_101d6370,&DAT_1017a514); + ExceptionList = local_10; + __security_check_cookie(local_14 ^ (uint)&stack0xfffffffc); + return; +} + + +``` + diff --git a/analysis/ghidra/exports/Lmx.dll.vtable-data-xrefs.md b/analysis/ghidra/exports/Lmx.dll.vtable-data-xrefs.md new file mode 100644 index 0000000..e7d277a --- /dev/null +++ b/analysis/ghidra/exports/Lmx.dll.vtable-data-xrefs.md @@ -0,0 +1,134 @@ +# Lmx.dll xrefs + +## 0x10196410 at 10196410 + +Target function: `(none)` + +| From | Ref type | Caller function | +| --- | --- | --- | +| (none) | | | + +## 0x10196400 at 10196400 + +Target function: `(none)` + +| From | Ref type | Caller function | +| --- | --- | --- | +| (none) | | | + +## 0x101963f8 at 101963f8 + +Target function: `(none)` + +| From | Ref type | Caller function | +| --- | --- | --- | +| (none) | | | + +## 0x101963f0 at 101963f0 + +Target function: `(none)` + +| From | Ref type | Caller function | +| --- | --- | --- | +| `10112dd3` | `DATA` | `FUN_10112da0` | +| `10113a13` | `DATA` | `FUN_101139c0` | +| `10113b5f` | `DATA` | `FUN_10113b10` | + +## 0x101963e8 at 101963e8 + +Target function: `(none)` + +| From | Ref type | Caller function | +| --- | --- | --- | +| `10112dda` | `DATA` | `FUN_10112da0` | +| `10113a1a` | `DATA` | `FUN_101139c0` | +| `10113b66` | `DATA` | `FUN_10113b10` | + +## 0x101963e0 at 101963e0 + +Target function: `(none)` + +| From | Ref type | Caller function | +| --- | --- | --- | +| (none) | | | + +## 0x10196418 at 10196418 + +Target function: `(none)` + +| From | Ref type | Caller function | +| --- | --- | --- | +| (none) | | | + +## 0x10196420 at 10196420 + +Target function: `(none)` + +| From | Ref type | Caller function | +| --- | --- | --- | +| (none) | | | + +## 0x1018f268 at 1018f268 + +Target function: `(none)` + +| From | Ref type | Caller function | +| --- | --- | --- | +| (none) | | | + +## 0x1018f260 at 1018f260 + +Target function: `(none)` + +| From | Ref type | Caller function | +| --- | --- | --- | +| (none) | | | + +## 0x1018f258 at 1018f258 + +Target function: `(none)` + +| From | Ref type | Caller function | +| --- | --- | --- | +| (none) | | | + +## 0x1018f270 at 1018f270 + +Target function: `(none)` + +| From | Ref type | Caller function | +| --- | --- | --- | +| (none) | | | + +## 0x10195488 at 10195488 + +Target function: `(none)` + +| From | Ref type | Caller function | +| --- | --- | --- | +| (none) | | | + +## 0x10195480 at 10195480 + +Target function: `(none)` + +| From | Ref type | Caller function | +| --- | --- | --- | +| (none) | | | + +## 0x10195478 at 10195478 + +Target function: `(none)` + +| From | Ref type | Caller function | +| --- | --- | --- | +| (none) | | | + +## 0x10195490 at 10195490 + +Target function: `(none)` + +| From | Ref type | Caller function | +| --- | --- | --- | +| (none) | | | + diff --git a/design/70-risks-and-open-questions.md b/design/70-risks-and-open-questions.md index 9b15953..31eb11d 100644 --- a/design/70-risks-and-open-questions.md +++ b/design/70-risks-and-open-questions.md @@ -40,46 +40,74 @@ The `OnBufferedDataChange` **public event shape** the wwtools api-notes describe **Settles when:** ✅ settled per option (a). Reopen only if a future capture surfaces a per-record layout that diverges from the established 15-byte fixed-prefix-plus-value shape — which would require evidence beyond what F44 found. -### R3 — `OperationComplete` trigger unproven **(settled 2026-05-06 — no mapping table exists; verbatim-preserve is the canonical answer)** +### R3 — `OperationComplete` trigger unproven **(settled 2026-05-06 — Path A landed: synthesizer kernel + typed `OperationStatus` events ported)** -**Severity: P1** (was a blocker; now settled per option: verbatim preserve is the canonical native behaviour) +**Severity: P1** (was a blocker; settled per Path A — typed promotion landed via `MxStatus::from_packed_u32`) -**Status (2026-05-06): SETTLED.** Five-stage Ghidra headless decompile traced the byte-to-`MXSTATUS_PROXY` synthesis path end-to-end across `Lmx.dll` and `LmxProxy.dll`. Logs: -- `analysis/ghidra/exports/Lmx.dll.aadct-decompile.md` — `aaDCT` symbol -- `analysis/ghidra/exports/LmxProxy.dll.completion-status-decompile.md` — Fire_* event handlers -- `analysis/ghidra/exports/LmxProxy.dll.fire-event-xrefs.md` — xrefs to Fire_* -- `analysis/ghidra/exports/LmxProxy.dll.status-synthesis-decompile.md` — Fire_* callers (`FUN_1001657f` / `FUN_10016b50` / `FUN_10016d4b`) -- `analysis/ghidra/exports/LmxProxy.dll.mxstatus-safearray-decompile.md` — `FUN_10003f60` (the SafeArray creator) -- `analysis/ghidra/exports/Lmx.dll.set-attribute-result-decompile.md` — `PreboundReference::OnSetAttributeResult` (`FUN_10114a90`) +**Status (2026-05-06): SETTLED PER PATH A.** The five-stage Ghidra walk that previously settled the verdict at "verbatim preserve" was extended with a sixth stage that found the actual byte→`MXSTATUS_PROXY` synthesizer. It is **`Lmx.dll!FUN_10100ce0`** — a single 4-byte u32 LE → `MxStatus` decoder used by every NMX-frame parser in `Lmx.dll`. Bit layout: + +``` +bit 31: success (-1 if set, 0 if clear) +bits 27..24: category (4 bits, masked by 0xF) +bits 23..20: detected_by (4 bits, masked by 0xF) +bits 15..0: detail (i16 — low 16 bits, signed) +bits 30..28, 19..16: reserved/padding +``` + +The Rust port now ships this kernel as [`MxStatus::from_packed_u32`] (and the inverse `to_packed_u32` for round-trip parity). `Session::operation_status_events()` emits typed [`OperationStatus`] events for every `0x32`/`0x33`-or-similar callback the wire delivers; the synthesizer is byte-deterministic and context-free, so the operation-tracking state machine the original verdict deferred is **not** required for the kernel itself. Per-operation context tracking (correlating completion frames back to outstanding writes/subscribes) is filed as a follow-up: see F54 below. + +A second mapping was also ported: `MxStatus::from_nmx_response_code` covers the constructed-from-response-code path in `Lmx.dll!FUN_1010bd10:741-770` (`ScanOnDemandCallback::GetResponse`), which builds an `MxStatus` from a 1-byte NMX `responseCode` field when no payload status word is present. Six proven mappings: `0x01`/`0x02` → `(CommunicationError, RequestingNmx)`, `0x03` → `(ConfigurationError, RequestingNmx)`, `0x04` → `(ConfigurationError, RespondingNmx)`, `0x05` → `(CommunicationError, RespondingNmx)`, `0x1A` → `(CommunicationError, RequestingNmx)`. Unmapped codes return `None` and the consumer falls back to verbatim preservation per CLAUDE.md "Do not fabricate protocol behavior." + +**What about the 1-byte completion frames `0x00`/`0x41`/`0xEF`?** Those are NOT decoded by `FUN_10100ce0` — they're a different wire field (the NMX operation-status callback payload, not the `INmxService.GetResponse2 responseCode` parameter). `Lmx.dll`'s decoder for those frames does not invoke any status-synthesis logic; they propagate as raw byte → `MxStatus { success: 0, Unknown, Unknown, detail: byte }`. The Rust port preserves this exactly. R4 is settled by the same fact (see below). + +Logs: +- `analysis/ghidra/exports/Lmx.dll.aadct-decompile.md` — `aaDCT` symbol (stage 1) +- `analysis/ghidra/exports/LmxProxy.dll.completion-status-decompile.md` — Fire_* event handlers (stage 2) +- `analysis/ghidra/exports/LmxProxy.dll.fire-event-xrefs.md` — xrefs to Fire_* (stage 3) +- `analysis/ghidra/exports/LmxProxy.dll.status-synthesis-decompile.md` — Fire_* callers (stage 4) +- `analysis/ghidra/exports/LmxProxy.dll.mxstatus-safearray-decompile.md` — `FUN_10003f60` (stage 5) +- `analysis/ghidra/exports/Lmx.dll.set-attribute-result-decompile.md` — `PreboundReference::OnSetAttributeResult` (stage 6, entry to next ring) +- `analysis/ghidra/exports/Lmx.dll.set-attribute-result-xrefs.md` — xrefs to `OnSetAttributeResult`/`CancelWithStatus`/`OperationComplete` (next-ring discovery) +- `analysis/ghidra/exports/Lmx.dll.synthesizer-decompile.md` — `ScanOnDemandCallback::OperationComplete`/`MultipleOperationComplete` (`FUN_1010b990`), `RemotePlatformResolver::OperationComplete` (`FUN_1010dc80`), and the constructed-from-responseCode synthesizer `FUN_1010bd10` (lines 698-770) +- `analysis/ghidra/exports/Lmx.dll.synthesizer-helpers-decompile.md` — `FUN_10003fc0` (the `` formatter), `FUN_1008f150` (the dispatch helper), `PreboundReference` constructors +- `analysis/ghidra/exports/Lmx.dll.synthesizer-helpers2-decompile.md` — **the synthesizer kernel `FUN_10100ce0`** (4-byte u32 → `MxStatus` decoder), `FUN_10100bc0` (3×u16 reader), `FUN_1005e580` (4-byte stream reader), `FUN_1010ee00` (sister NMX-frame parser using the same kernel) +- `analysis/ghidra/exports/Lmx.dll.synthesizer-callers-xrefs.md` — caller graph for the synthesizer ring Findings, layer by layer (the wire bytes flow inward; the synthesis flows outward): 1. **`Lmx.aaDCT`** at `0x10178fc0` is a `SysAllocString(L"Lmx.aaDCT")` into a global BSTR — a tracing category name, not a status-mapping table. No array / lookup logic. 2. **`MXSTATUS_PROXY`** (16 bytes, Pack=4) is a 4-field marshalled struct: `success: i16` at offset 0, `category: i16` at offset 4, `detectedBy: i16` at offset 8, `detail: i16` at offset 12. It is the *output* of synthesis, not a lookup-table entry. 3. **`LmxProxy.dll` Fire_* event handlers** (`FUN_10015f72`, `FUN_1001611f`, `FUN_10016271`, `FUN_100163c0`) take an *already-populated* `MXSTATUS_PROXY[]` and forward it through ATL connection-point dispatch. No synthesis here. -4. **`LmxProxy.dll` Fire_* callers** (`FUN_1001657f` for OnDataChange / OnBufferedDataChange, `FUN_10016b50` for OnWriteComplete, `FUN_10016d4b` for OperationComplete) call **`FUN_10003f60(out_safearray, in_status_ptr, count=1)`** which creates the SafeArray. `FUN_10003f60` is **a verbatim memcpy** of an existing 14-byte buffer into the SAFEARRAY data — no transformation. Source confirms: bytes flow `*local_8 = *param_2; *(local_8+2) = *(param_2+2); *(local_8+4) = *(param_2+4); local_8[6] = param_2[6]`. -5. **`Lmx.dll` `PreboundReference::OnSetAttributeResult`** (`FUN_10114a90`) — the CALLER of step 4's path — receives an already-populated `short *param_7` status buffer. Its log line confirms the layout: `swprintf_s(L"", (i16)*p, *(p+2), *(p+4), p[6])`. Its dispatch logic checks typed values (`*local_b6c == -1`, `*(int *)(local_b6c + 2) == 3`) — synthesis is upstream of THIS function too. +4. **`LmxProxy.dll` Fire_* callers** (`FUN_1001657f` for OnDataChange / OnBufferedDataChange, `FUN_10016b50` for OnWriteComplete, `FUN_10016d4b` for OperationComplete) call **`FUN_10003f60(out_safearray, in_status_ptr, count=1)`** which creates the SafeArray. `FUN_10003f60` is **a verbatim memcpy** of an existing 14-byte buffer into the SAFEARRAY data — no transformation. +5. **`Lmx.dll` `PreboundReference::OnSetAttributeResult`** (`FUN_10114a90`) — the CALLER of step 4's path — receives an already-populated `short *param_7` status buffer; synthesis is upstream of THIS function too. +6. **The synthesizer kernel itself**: **`Lmx.dll!FUN_10100ce0`** (see `analysis/ghidra/exports/Lmx.dll.synthesizer-helpers2-decompile.md`). A 4-byte u32 LE read from a stream → 4-tuple `MxStatus` decoder. Pure transformation, no operation-context dependency. Used by every NMX-frame parser in `Lmx.dll` (`FUN_1010bd10` `ScanOnDemandCallback::GetResponse`, `FUN_1010ee00` `AccessManager::ProcessNmxRequest`, `FUN_10110986`, etc.) — the upstream decoder reads the wire bytes, the kernel translates them. +7. **The constructed-when-no-bytes path**: when an NMX `responseCode != 0` arrives without a payload status word, `FUN_1010bd10:741-770` constructs an `MxStatus` from the responseCode itself via a fixed switch. Six proven response codes (1, 2, 3, 4, 5, 0x1A); see the table in the `MxStatus::from_nmx_response_code` doc. -**The synthesizer is the NMX-frame decoder in `Lmx.dll`** that calls `OnSetAttributeResult` / `OnGetAttributeResult` / equivalent OperationComplete handler. That decoder takes raw NMX bytes (e.g. 1-byte `0x00`/`0x41`/`0xEF` completion frames or 5-byte `00 00 50 80 00`-style status words) plus operation context (which item, which engine, retry state, last-write-correlation-id) and computes the populated `MXSTATUS_PROXY`. **There is no static lookup table** — the synthesis is per-message contextual. +**Path A landed.** The synthesizer kernel and the constructed-from-response-code switch were both portable as pure functions — no operation-tracking state machine required for the kernel itself, because `FUN_10100ce0` is byte-deterministic. Rust port: -**Why this means R3/R4 stay at "verbatim preserve" canonically.** Two viable paths exist if a future consumer demands typed promotion (neither is a small Rust patch): +- `mxaccess-codec::status::MxStatus::from_packed_u32(packed: u32) -> MxStatus` — the kernel. +- `mxaccess-codec::status::MxStatus::to_packed_u32() -> u32` — inverse, for round-trip parity. +- `mxaccess-codec::status::MxStatus::from_nmx_response_code(byte: u8) -> Option` — the response-code switch. +- `mxaccess::OperationKind` + `mxaccess::OperationContext` types for future correlation work (per-operation tracking is filed as F54). +- `mxaccess::Session::operation_status_events()` returns `broadcast::Receiver>`; `operation_status_stream()` returns the `Stream>` variant. +- `mxaccess::OperationStatus { raw, status, context, is_during_recovery }` — matches `MxNativeOperationStatusEvent` (`MxNativeSession.cs:73-78`) plus typed `MxStatus` promotion. +- The callback router (`session::callback_router`) now tries operation-status parsing first, mirroring `MxNativeSession.OnCallbackReceived:574`. -- **Path A — port the synthesizer.** Find every NMX-decoder callsite of `OnSetAttributeResult`/`OnGetAttributeResult`/`OperationComplete` in `Lmx.dll` (next xref ring beyond `FUN_10114a90`); decompile each; reverse-engineer the per-decoder synthesis logic; port to Rust. The synthesis depends on operation-tracking state (item handles, retry counters, correlation ids) the Rust codec does not currently track — so the port is more than a codec change; it's a session-state-machine extension. Estimate: ~1-2 weeks of focused work. -- **Path B — empirical capture pairs.** Run the .NET probe in scenarios that produce each completion byte; capture the (input NMX bytes, observed `MXSTATUS_PROXY`) pair via Frida hooks on `LmxProxy.dll!FUN_10003f60`; build an empirical (byte + context → status) mapping. ~30 min × 6-10 scenarios. Output: a mapping table that approximates the synthesizer without re-implementing it. Risk: the mapping is only valid for the captured operation contexts; new contexts may produce statuses outside the table. +**What about the 1-byte completion frames `0x00`/`0x41`/`0xEF`?** They are NOT decoded by `FUN_10100ce0` (they're a different wire field at a different layer — the NMX operation-status callback payload, not the `INmxService.GetResponse2` responseCode parameter). Per CLAUDE.md "Do not fabricate protocol behavior" they continue to propagate as `MxStatus { success: 0, Unknown, Unknown, detail: byte }`. R4 is settled by the same fact. -**Current best answer:** unchanged — `Session::operation_status_events()` exposes `Stream` carrying frame bytes. Promote to a typed `WriteCompleted` only on the proven `00 00 50 80 00` 5-byte pattern. Other bytes stay raw as `MxStatus { Success: 0, Category: Unknown, DetectedBy: Unknown, Detail: byte }`. The Rust codec mirrors `src/MxNativeCodec/NmxOperationStatusMessage.cs:TryParseInner`. The .NET reference does the same, for the same reason: the synthesizer is too context-dependent to mirror without porting the entire operation-tracking state machine, and that exceeds V1 scope. +**Current best answer:** Path A landed. `Session::operation_status_events()` emits typed `OperationStatus` events. The synthesizer kernel (`MxStatus::from_packed_u32`) is exposed for any consumer that holds a 4-byte packed status word (e.g. extracted from a subscription record's `status: i32` field). Per-operation context (correlating completion frames back to outstanding writes/subscribes) is the next step — filed as F54. -**Reopen when:** either (a) a consumer files a concrete use case for typed promotion of a specific byte+context combination — at which point Path B's empirical capture for that one combination is the cheapest answer; or (b) a major-version bump justifies the operation-tracking state-machine port (Path A). Until then, verbatim preservation is correct by construction. +**Reopen when:** F54 lands per-operation correlation, or a future capture surfaces a fresh wire field whose synthesis logic doesn't reduce to `FUN_10100ce0` + `from_nmx_response_code` (no such field has been observed to date). -### R4 — Completion-only byte mapping **(settled 2026-05-06 — collapses into R3's resolution)** +### R4 — Completion-only byte mapping **(settled 2026-05-06 — verbatim-preserve confirmed; synthesizer doesn't apply at this layer)** -**Severity: P1** (was a blocker; now settled per the same R3 finding) +**Severity: P1** (was a blocker; now settled per the same R3 Path A finding — by exclusion) -**Status (2026-05-06): SETTLED.** Same Ghidra walk as R3. The 1-byte completion frames `0x00`, `0x41`, `0xEF` (`work_remain.md:164–174`) are the same intermediate NMX signals that R3 covers. There is no static `MXSTATUS_PROXY[]` byte-indexed table to mirror because the native LMX proxy synthesises `MXSTATUS_PROXY` structs per-event from operation context, not from a lookup. +**Status (2026-05-06): SETTLED.** R3's Path A walk traced the byte→`MxStatus` synthesizer to **`Lmx.dll!FUN_10100ce0`**, a 4-byte u32 LE → `MxStatus` decoder. The 1-byte completion frames `0x00`, `0x41`, `0xEF` (`work_remain.md:164–174`) are NOT input to that decoder — they're a different wire field, observed at a different layer (the NMX operation-status callback payload, not the `INmxService.GetResponse2` responseCode parameter or any 4-byte packed status field). `Lmx.dll`'s decoder for the 1-byte completion-only inner body does not invoke any synthesis logic; the bytes propagate untransformed. -**Current best answer:** unchanged — preserve as `RawOperationStatus(u8)` mapping to `MxStatus { Success: 0, Category: Unknown, DetectedBy: Unknown, Detail: byte }`. The .NET reference does the same; the Rust port matches. +**Current best answer:** unchanged — preserve as `MxStatus { Success: 0, Category: Unknown, DetectedBy: Unknown, Detail: byte }`. `mxaccess-codec::NmxOperationStatusMessage::promote_to_typed` returns the verbatim placeholder for these frames; `mxaccess::Session::operation_status_events()` surfaces them via the typed `OperationStatus.status` field with the byte preserved in `detail`. -**Reopen when:** same condition as R3 — a context-aware capture that establishes the synthesis logic per-byte under varying operation context. +**Reopen when:** a fresh capture proves a synthesis rule for a specific 1-byte completion code under a specific operation context (e.g. via Frida pairs `LmxProxy.dll!FUN_10003f60` input vs. observed event payload). At that point file a sub-followup with the captured `(byte, context, observed status)` triple and decide whether to add a typed mapping. ### R5 — Activate / Suspend behaviour **(partially observed — F44 documented client-side trigger; wire-side residual gap filed as F46, hook landed pending live re-run)** diff --git a/design/followups.md b/design/followups.md index b549d4d..d964df0 100644 --- a/design/followups.md +++ b/design/followups.md @@ -80,6 +80,24 @@ Between each publish: wait for the crate to be indexed before the next one's `ca **Resolves when:** all three optimisations land or are deliberately rejected with a note in the baseline doc. +### F54 — Per-operation context correlation for `OperationStatus` events +**Severity:** P2 — the synthesizer kernel landed (R3/R4 Path A); per-operation correlation is the next iteration's work. +**Source:** R3/R4 Path A closeout (`design/70-risks-and-open-questions.md`). The Path A walk found `Lmx.dll!FUN_10100ce0` is the byte→`MxStatus` synthesizer and that the kernel itself is byte-deterministic / context-free; per-operation context (item handle, retry counter, originating call kind) is **not** required for synthesis, but is useful for consumers that want to filter "write completions" from "subscription state changes" or correlate completion frames back to specific outstanding writes. + +**Scope.** +1. Add an `outstanding_operations: tokio::sync::Mutex>` registry on `SessionInner`, parallel to the existing `subscriptions` registry. +2. Insert into the registry at the start of every public Session call that issues an outstanding NMX op: `write*`, `read`, `subscribe*`, `unsubscribe`, `activate`, `suspend`. Key by the 16-byte correlation id the call generates. Mirror the .NET reference's private `_pendingWrites`/`_pendingReads` dictionaries. +3. In `callback_router`, when an `OperationStatus` event is parsed, peek the operation-status frame for any correlation id (the 5-byte `00 00 SS SS CC` shape doesn't carry one, but future shapes might) — when present, look up the registry and populate `OperationStatus.context`. When absent, leave `context = None`. +4. Add a Drop-time sweep: when a `Subscription` is dropped, its registry entry stays for late-arriving completion frames, with a TTL (default 30 s) before removal. Mirrors the .NET reference's "completed" dictionary. +5. Round-trip tests: synthesize a `0x32` callback with a known correlation id, hand-insert a registry entry, assert the emitted `OperationStatus.context` matches. + +**Definition of done:** +1. `Session::operation_status_events()` emits `OperationStatus.context = Some(_)` for at least the subscription-state-change path (`0x32` SubscriptionStatus frames carry the item correlation id, which the registry will already hold). +2. Round-trip tests demonstrate the populated-context path. +3. R3 in `70-risks-and-open-questions.md` updated from "Path A landed (kernel only)" to "Path A complete (kernel + correlation)". + +**Resolves when:** the registry lives and at least one wire path emits a populated `context`. + ### F53 — Enable `#![warn(missing_docs)]` workspace-wide **Severity:** P3 — doc-coverage tightening; not a correctness or release blocker. **Source:** F42 closeout — the missing-docs lint was deferred because enabling it surfaces hundreds of low-priority public-item gaps that are out of scope for that F-number. diff --git a/design/public-api/mxaccess-codec.txt b/design/public-api/mxaccess-codec.txt index e076d91..0b87130 100644 --- a/design/public-api/mxaccess-codec.txt +++ b/design/public-api/mxaccess-codec.txt @@ -514,7 +514,9 @@ pub mxaccess_codec::operation_status::NmxOperationStatusMessage::status: mxacces pub mxaccess_codec::operation_status::NmxOperationStatusMessage::status_code: u16 impl mxaccess_codec::operation_status::NmxOperationStatusMessage pub fn mxaccess_codec::operation_status::NmxOperationStatusMessage::is_mx_access_write_complete(&self) -> bool +pub const fn mxaccess_codec::operation_status::NmxOperationStatusMessage::promote_to_typed(&self) -> mxaccess_codec::status::MxStatus pub fn mxaccess_codec::operation_status::NmxOperationStatusMessage::try_parse_inner(inner: &[u8]) -> core::result::Result +pub fn mxaccess_codec::operation_status::NmxOperationStatusMessage::try_parse_process_data_received_body(body: &[u8]) -> core::result::Result impl core::clone::Clone for mxaccess_codec::operation_status::NmxOperationStatusMessage pub fn mxaccess_codec::operation_status::NmxOperationStatusMessage::clone(&self) -> mxaccess_codec::operation_status::NmxOperationStatusMessage impl core::cmp::Eq for mxaccess_codec::operation_status::NmxOperationStatusMessage @@ -618,8 +620,8 @@ pub mxaccess_codec::prelude::MxStatusCategory::SoftwareError = 7 pub mxaccess_codec::prelude::MxStatusCategory::Unknown = -1 pub mxaccess_codec::prelude::MxStatusCategory::Warning = 2 impl mxaccess_codec::status::MxStatusCategory -pub fn mxaccess_codec::status::MxStatusCategory::from_i16(value: i16) -> Self -pub fn mxaccess_codec::status::MxStatusCategory::to_i16(self) -> i16 +pub const fn mxaccess_codec::status::MxStatusCategory::from_i16(value: i16) -> Self +pub const fn mxaccess_codec::status::MxStatusCategory::to_i16(self) -> i16 impl core::clone::Clone for mxaccess_codec::status::MxStatusCategory pub fn mxaccess_codec::status::MxStatusCategory::clone(&self) -> mxaccess_codec::status::MxStatusCategory impl core::cmp::Eq for mxaccess_codec::status::MxStatusCategory @@ -649,8 +651,8 @@ pub mxaccess_codec::prelude::MxStatusSource::RespondingLmx = 1 pub mxaccess_codec::prelude::MxStatusSource::RespondingNmx = 3 pub mxaccess_codec::prelude::MxStatusSource::Unknown = -1 impl mxaccess_codec::status::MxStatusSource -pub fn mxaccess_codec::status::MxStatusSource::from_i16(value: i16) -> Self -pub fn mxaccess_codec::status::MxStatusSource::to_i16(self) -> i16 +pub const fn mxaccess_codec::status::MxStatusSource::from_i16(value: i16) -> Self +pub const fn mxaccess_codec::status::MxStatusSource::to_i16(self) -> i16 impl core::clone::Clone for mxaccess_codec::status::MxStatusSource pub fn mxaccess_codec::status::MxStatusSource::clone(&self) -> mxaccess_codec::status::MxStatusSource impl core::cmp::Eq for mxaccess_codec::status::MxStatusSource @@ -838,7 +840,10 @@ pub const mxaccess_codec::status::MxStatus::INVALID_REFERENCE_CONFIGURATION: Sel pub const mxaccess_codec::status::MxStatus::SUSPEND_PENDING: Self pub const mxaccess_codec::status::MxStatus::WRITE_COMPLETE_OK: Self pub fn mxaccess_codec::status::MxStatus::detail_text(&self) -> core::option::Option<&'static str> +pub const fn mxaccess_codec::status::MxStatus::from_nmx_response_code(response_code: u8) -> core::option::Option +pub const fn mxaccess_codec::status::MxStatus::from_packed_u32(packed: u32) -> Self pub fn mxaccess_codec::status::MxStatus::is_ok(&self) -> bool +pub const fn mxaccess_codec::status::MxStatus::to_packed_u32(self) -> u32 impl core::clone::Clone for mxaccess_codec::status::MxStatus pub fn mxaccess_codec::status::MxStatus::clone(&self) -> mxaccess_codec::status::MxStatus impl core::cmp::Eq for mxaccess_codec::status::MxStatus @@ -903,7 +908,9 @@ pub mxaccess_codec::prelude::NmxOperationStatusMessage::status: mxaccess_codec:: pub mxaccess_codec::prelude::NmxOperationStatusMessage::status_code: u16 impl mxaccess_codec::operation_status::NmxOperationStatusMessage pub fn mxaccess_codec::operation_status::NmxOperationStatusMessage::is_mx_access_write_complete(&self) -> bool +pub const fn mxaccess_codec::operation_status::NmxOperationStatusMessage::promote_to_typed(&self) -> mxaccess_codec::status::MxStatus pub fn mxaccess_codec::operation_status::NmxOperationStatusMessage::try_parse_inner(inner: &[u8]) -> core::result::Result +pub fn mxaccess_codec::operation_status::NmxOperationStatusMessage::try_parse_process_data_received_body(body: &[u8]) -> core::result::Result impl core::clone::Clone for mxaccess_codec::operation_status::NmxOperationStatusMessage pub fn mxaccess_codec::operation_status::NmxOperationStatusMessage::clone(&self) -> mxaccess_codec::operation_status::NmxOperationStatusMessage impl core::cmp::Eq for mxaccess_codec::operation_status::NmxOperationStatusMessage @@ -1220,8 +1227,8 @@ pub mxaccess_codec::status::MxStatusCategory::SoftwareError = 7 pub mxaccess_codec::status::MxStatusCategory::Unknown = -1 pub mxaccess_codec::status::MxStatusCategory::Warning = 2 impl mxaccess_codec::status::MxStatusCategory -pub fn mxaccess_codec::status::MxStatusCategory::from_i16(value: i16) -> Self -pub fn mxaccess_codec::status::MxStatusCategory::to_i16(self) -> i16 +pub const fn mxaccess_codec::status::MxStatusCategory::from_i16(value: i16) -> Self +pub const fn mxaccess_codec::status::MxStatusCategory::to_i16(self) -> i16 impl core::clone::Clone for mxaccess_codec::status::MxStatusCategory pub fn mxaccess_codec::status::MxStatusCategory::clone(&self) -> mxaccess_codec::status::MxStatusCategory impl core::cmp::Eq for mxaccess_codec::status::MxStatusCategory @@ -1251,8 +1258,8 @@ pub mxaccess_codec::status::MxStatusSource::RespondingLmx = 1 pub mxaccess_codec::status::MxStatusSource::RespondingNmx = 3 pub mxaccess_codec::status::MxStatusSource::Unknown = -1 impl mxaccess_codec::status::MxStatusSource -pub fn mxaccess_codec::status::MxStatusSource::from_i16(value: i16) -> Self -pub fn mxaccess_codec::status::MxStatusSource::to_i16(self) -> i16 +pub const fn mxaccess_codec::status::MxStatusSource::from_i16(value: i16) -> Self +pub const fn mxaccess_codec::status::MxStatusSource::to_i16(self) -> i16 impl core::clone::Clone for mxaccess_codec::status::MxStatusSource pub fn mxaccess_codec::status::MxStatusSource::clone(&self) -> mxaccess_codec::status::MxStatusSource impl core::cmp::Eq for mxaccess_codec::status::MxStatusSource @@ -1285,7 +1292,10 @@ pub const mxaccess_codec::status::MxStatus::INVALID_REFERENCE_CONFIGURATION: Sel pub const mxaccess_codec::status::MxStatus::SUSPEND_PENDING: Self pub const mxaccess_codec::status::MxStatus::WRITE_COMPLETE_OK: Self pub fn mxaccess_codec::status::MxStatus::detail_text(&self) -> core::option::Option<&'static str> +pub const fn mxaccess_codec::status::MxStatus::from_nmx_response_code(response_code: u8) -> core::option::Option +pub const fn mxaccess_codec::status::MxStatus::from_packed_u32(packed: u32) -> Self pub fn mxaccess_codec::status::MxStatus::is_ok(&self) -> bool +pub const fn mxaccess_codec::status::MxStatus::to_packed_u32(self) -> u32 impl core::clone::Clone for mxaccess_codec::status::MxStatus pub fn mxaccess_codec::status::MxStatus::clone(&self) -> mxaccess_codec::status::MxStatus impl core::cmp::Eq for mxaccess_codec::status::MxStatus @@ -1774,8 +1784,8 @@ pub mxaccess_codec::MxStatusCategory::SoftwareError = 7 pub mxaccess_codec::MxStatusCategory::Unknown = -1 pub mxaccess_codec::MxStatusCategory::Warning = 2 impl mxaccess_codec::status::MxStatusCategory -pub fn mxaccess_codec::status::MxStatusCategory::from_i16(value: i16) -> Self -pub fn mxaccess_codec::status::MxStatusCategory::to_i16(self) -> i16 +pub const fn mxaccess_codec::status::MxStatusCategory::from_i16(value: i16) -> Self +pub const fn mxaccess_codec::status::MxStatusCategory::to_i16(self) -> i16 impl core::clone::Clone for mxaccess_codec::status::MxStatusCategory pub fn mxaccess_codec::status::MxStatusCategory::clone(&self) -> mxaccess_codec::status::MxStatusCategory impl core::cmp::Eq for mxaccess_codec::status::MxStatusCategory @@ -1805,8 +1815,8 @@ pub mxaccess_codec::MxStatusSource::RespondingLmx = 1 pub mxaccess_codec::MxStatusSource::RespondingNmx = 3 pub mxaccess_codec::MxStatusSource::Unknown = -1 impl mxaccess_codec::status::MxStatusSource -pub fn mxaccess_codec::status::MxStatusSource::from_i16(value: i16) -> Self -pub fn mxaccess_codec::status::MxStatusSource::to_i16(self) -> i16 +pub const fn mxaccess_codec::status::MxStatusSource::from_i16(value: i16) -> Self +pub const fn mxaccess_codec::status::MxStatusSource::to_i16(self) -> i16 impl core::clone::Clone for mxaccess_codec::status::MxStatusSource pub fn mxaccess_codec::status::MxStatusSource::clone(&self) -> mxaccess_codec::status::MxStatusSource impl core::cmp::Eq for mxaccess_codec::status::MxStatusSource @@ -2100,7 +2110,10 @@ pub const mxaccess_codec::status::MxStatus::INVALID_REFERENCE_CONFIGURATION: Sel pub const mxaccess_codec::status::MxStatus::SUSPEND_PENDING: Self pub const mxaccess_codec::status::MxStatus::WRITE_COMPLETE_OK: Self pub fn mxaccess_codec::status::MxStatus::detail_text(&self) -> core::option::Option<&'static str> +pub const fn mxaccess_codec::status::MxStatus::from_nmx_response_code(response_code: u8) -> core::option::Option +pub const fn mxaccess_codec::status::MxStatus::from_packed_u32(packed: u32) -> Self pub fn mxaccess_codec::status::MxStatus::is_ok(&self) -> bool +pub const fn mxaccess_codec::status::MxStatus::to_packed_u32(self) -> u32 impl core::clone::Clone for mxaccess_codec::status::MxStatus pub fn mxaccess_codec::status::MxStatus::clone(&self) -> mxaccess_codec::status::MxStatus impl core::cmp::Eq for mxaccess_codec::status::MxStatus @@ -2244,7 +2257,9 @@ pub mxaccess_codec::NmxOperationStatusMessage::status: mxaccess_codec::status::M pub mxaccess_codec::NmxOperationStatusMessage::status_code: u16 impl mxaccess_codec::operation_status::NmxOperationStatusMessage pub fn mxaccess_codec::operation_status::NmxOperationStatusMessage::is_mx_access_write_complete(&self) -> bool +pub const fn mxaccess_codec::operation_status::NmxOperationStatusMessage::promote_to_typed(&self) -> mxaccess_codec::status::MxStatus pub fn mxaccess_codec::operation_status::NmxOperationStatusMessage::try_parse_inner(inner: &[u8]) -> core::result::Result +pub fn mxaccess_codec::operation_status::NmxOperationStatusMessage::try_parse_process_data_received_body(body: &[u8]) -> core::result::Result impl core::clone::Clone for mxaccess_codec::operation_status::NmxOperationStatusMessage pub fn mxaccess_codec::operation_status::NmxOperationStatusMessage::clone(&self) -> mxaccess_codec::operation_status::NmxOperationStatusMessage impl core::cmp::Eq for mxaccess_codec::operation_status::NmxOperationStatusMessage diff --git a/design/public-api/mxaccess.txt b/design/public-api/mxaccess.txt index 4076262..67b66c6 100644 --- a/design/public-api/mxaccess.txt +++ b/design/public-api/mxaccess.txt @@ -57,6 +57,65 @@ impl core::marker::UnsafeUnpin for mxaccess::asb_session::AsbSubscription impl core::panic::unwind_safe::RefUnwindSafe for mxaccess::asb_session::AsbSubscription impl core::panic::unwind_safe::UnwindSafe for mxaccess::asb_session::AsbSubscription pub mod mxaccess::session +#[non_exhaustive] pub enum mxaccess::session::OperationKind +pub mxaccess::session::OperationKind::Activate +pub mxaccess::session::OperationKind::Other +pub mxaccess::session::OperationKind::Read +pub mxaccess::session::OperationKind::Subscribe +pub mxaccess::session::OperationKind::Suspend +pub mxaccess::session::OperationKind::Unsubscribe +pub mxaccess::session::OperationKind::Write +pub mxaccess::session::OperationKind::WriteSecured +impl core::clone::Clone for mxaccess::session::OperationKind +pub fn mxaccess::session::OperationKind::clone(&self) -> mxaccess::session::OperationKind +impl core::cmp::Eq for mxaccess::session::OperationKind +impl core::cmp::PartialEq for mxaccess::session::OperationKind +pub fn mxaccess::session::OperationKind::eq(&self, other: &mxaccess::session::OperationKind) -> bool +impl core::fmt::Debug for mxaccess::session::OperationKind +pub fn mxaccess::session::OperationKind::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::hash::Hash for mxaccess::session::OperationKind +pub fn mxaccess::session::OperationKind::hash<__H: core::hash::Hasher>(&self, state: &mut __H) +impl core::marker::Copy for mxaccess::session::OperationKind +impl core::marker::StructuralPartialEq for mxaccess::session::OperationKind +impl core::marker::Freeze for mxaccess::session::OperationKind +impl core::marker::Send for mxaccess::session::OperationKind +impl core::marker::Sync for mxaccess::session::OperationKind +impl core::marker::Unpin for mxaccess::session::OperationKind +impl core::marker::UnsafeUnpin for mxaccess::session::OperationKind +impl core::panic::unwind_safe::RefUnwindSafe for mxaccess::session::OperationKind +impl core::panic::unwind_safe::UnwindSafe for mxaccess::session::OperationKind +#[non_exhaustive] pub struct mxaccess::session::OperationContext +pub mxaccess::session::OperationContext::correlation_id: [u8; 16] +pub mxaccess::session::OperationContext::op_kind: mxaccess::session::OperationKind +pub mxaccess::session::OperationContext::reference: core::option::Option> +pub mxaccess::session::OperationContext::retry_count: u32 +impl core::clone::Clone for mxaccess::session::OperationContext +pub fn mxaccess::session::OperationContext::clone(&self) -> mxaccess::session::OperationContext +impl core::fmt::Debug for mxaccess::session::OperationContext +pub fn mxaccess::session::OperationContext::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Freeze for mxaccess::session::OperationContext +impl core::marker::Send for mxaccess::session::OperationContext +impl core::marker::Sync for mxaccess::session::OperationContext +impl core::marker::Unpin for mxaccess::session::OperationContext +impl core::marker::UnsafeUnpin for mxaccess::session::OperationContext +impl core::panic::unwind_safe::RefUnwindSafe for mxaccess::session::OperationContext +impl core::panic::unwind_safe::UnwindSafe for mxaccess::session::OperationContext +#[non_exhaustive] pub struct mxaccess::session::OperationStatus +pub mxaccess::session::OperationStatus::context: core::option::Option +pub mxaccess::session::OperationStatus::is_during_recovery: bool +pub mxaccess::session::OperationStatus::raw: mxaccess_codec::operation_status::NmxOperationStatusMessage +pub mxaccess::session::OperationStatus::status: mxaccess_codec::status::MxStatus +impl core::clone::Clone for mxaccess::session::OperationStatus +pub fn mxaccess::session::OperationStatus::clone(&self) -> mxaccess::session::OperationStatus +impl core::fmt::Debug for mxaccess::session::OperationStatus +pub fn mxaccess::session::OperationStatus::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Freeze for mxaccess::session::OperationStatus +impl core::marker::Send for mxaccess::session::OperationStatus +impl core::marker::Sync for mxaccess::session::OperationStatus +impl core::marker::Unpin for mxaccess::session::OperationStatus +impl core::marker::UnsafeUnpin for mxaccess::session::OperationStatus +impl core::panic::unwind_safe::RefUnwindSafe for mxaccess::session::OperationStatus +impl core::panic::unwind_safe::UnwindSafe for mxaccess::session::OperationStatus pub struct mxaccess::session::SessionInner impl core::fmt::Debug for mxaccess::session::SessionInner pub fn mxaccess::session::SessionInner::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result @@ -208,6 +267,33 @@ impl core::marker::Unpin for mxaccess::Error impl core::marker::UnsafeUnpin for mxaccess::Error impl !core::panic::unwind_safe::RefUnwindSafe for mxaccess::Error impl !core::panic::unwind_safe::UnwindSafe for mxaccess::Error +#[non_exhaustive] pub enum mxaccess::OperationKind +pub mxaccess::OperationKind::Activate +pub mxaccess::OperationKind::Other +pub mxaccess::OperationKind::Read +pub mxaccess::OperationKind::Subscribe +pub mxaccess::OperationKind::Suspend +pub mxaccess::OperationKind::Unsubscribe +pub mxaccess::OperationKind::Write +pub mxaccess::OperationKind::WriteSecured +impl core::clone::Clone for mxaccess::session::OperationKind +pub fn mxaccess::session::OperationKind::clone(&self) -> mxaccess::session::OperationKind +impl core::cmp::Eq for mxaccess::session::OperationKind +impl core::cmp::PartialEq for mxaccess::session::OperationKind +pub fn mxaccess::session::OperationKind::eq(&self, other: &mxaccess::session::OperationKind) -> bool +impl core::fmt::Debug for mxaccess::session::OperationKind +pub fn mxaccess::session::OperationKind::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::hash::Hash for mxaccess::session::OperationKind +pub fn mxaccess::session::OperationKind::hash<__H: core::hash::Hasher>(&self, state: &mut __H) +impl core::marker::Copy for mxaccess::session::OperationKind +impl core::marker::StructuralPartialEq for mxaccess::session::OperationKind +impl core::marker::Freeze for mxaccess::session::OperationKind +impl core::marker::Send for mxaccess::session::OperationKind +impl core::marker::Sync for mxaccess::session::OperationKind +impl core::marker::Unpin for mxaccess::session::OperationKind +impl core::marker::UnsafeUnpin for mxaccess::session::OperationKind +impl core::panic::unwind_safe::RefUnwindSafe for mxaccess::session::OperationKind +impl core::panic::unwind_safe::UnwindSafe for mxaccess::session::OperationKind #[non_exhaustive] pub enum mxaccess::ProtocolError pub mxaccess::ProtocolError::Decode pub mxaccess::ProtocolError::Decode::buffer_len: usize @@ -391,6 +477,38 @@ impl core::marker::Unpin for mxaccess::DataChange impl core::marker::UnsafeUnpin for mxaccess::DataChange impl core::panic::unwind_safe::RefUnwindSafe for mxaccess::DataChange impl core::panic::unwind_safe::UnwindSafe for mxaccess::DataChange +#[non_exhaustive] pub struct mxaccess::OperationContext +pub mxaccess::OperationContext::correlation_id: [u8; 16] +pub mxaccess::OperationContext::op_kind: mxaccess::session::OperationKind +pub mxaccess::OperationContext::reference: core::option::Option> +pub mxaccess::OperationContext::retry_count: u32 +impl core::clone::Clone for mxaccess::session::OperationContext +pub fn mxaccess::session::OperationContext::clone(&self) -> mxaccess::session::OperationContext +impl core::fmt::Debug for mxaccess::session::OperationContext +pub fn mxaccess::session::OperationContext::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Freeze for mxaccess::session::OperationContext +impl core::marker::Send for mxaccess::session::OperationContext +impl core::marker::Sync for mxaccess::session::OperationContext +impl core::marker::Unpin for mxaccess::session::OperationContext +impl core::marker::UnsafeUnpin for mxaccess::session::OperationContext +impl core::panic::unwind_safe::RefUnwindSafe for mxaccess::session::OperationContext +impl core::panic::unwind_safe::UnwindSafe for mxaccess::session::OperationContext +#[non_exhaustive] pub struct mxaccess::OperationStatus +pub mxaccess::OperationStatus::context: core::option::Option +pub mxaccess::OperationStatus::is_during_recovery: bool +pub mxaccess::OperationStatus::raw: mxaccess_codec::operation_status::NmxOperationStatusMessage +pub mxaccess::OperationStatus::status: mxaccess_codec::status::MxStatus +impl core::clone::Clone for mxaccess::session::OperationStatus +pub fn mxaccess::session::OperationStatus::clone(&self) -> mxaccess::session::OperationStatus +impl core::fmt::Debug for mxaccess::session::OperationStatus +pub fn mxaccess::session::OperationStatus::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Freeze for mxaccess::session::OperationStatus +impl core::marker::Send for mxaccess::session::OperationStatus +impl core::marker::Sync for mxaccess::session::OperationStatus +impl core::marker::Unpin for mxaccess::session::OperationStatus +impl core::marker::UnsafeUnpin for mxaccess::session::OperationStatus +impl core::panic::unwind_safe::RefUnwindSafe for mxaccess::session::OperationStatus +impl core::panic::unwind_safe::UnwindSafe for mxaccess::session::OperationStatus pub struct mxaccess::RecoveryPolicy pub mxaccess::RecoveryPolicy::delay: core::time::Duration pub mxaccess::RecoveryPolicy::max_attempts: u32 @@ -437,6 +555,8 @@ pub async fn mxaccess::Session::callback_exporter_addr(&self) -> core::option::O pub fn mxaccess::Session::callbacks(&self) -> tokio::sync::broadcast::Receiver> pub async fn mxaccess::Session::connect_nmx(addr: core::net::socket_addr::SocketAddr, options: mxaccess::SessionOptions, ntlm: mxaccess_rpc::ntlm::NtlmClientContext, service_ipid: mxaccess_rpc::guid::Guid, resolver: alloc::sync::Arc, recovery: mxaccess::RecoveryPolicy) -> core::result::Result pub async fn mxaccess::Session::has_recovery_factory(&self) -> bool +pub fn mxaccess::Session::operation_status_events(&self) -> tokio::sync::broadcast::Receiver> +pub fn mxaccess::Session::operation_status_stream(&self) -> impl futures_core::stream::Stream, mxaccess::Error>> + core::marker::Send pub async fn mxaccess::Session::read(&self, reference: &str, timeout: core::time::Duration) -> core::result::Result pub async fn mxaccess::Session::recover_connection(&self, policy: mxaccess::RecoveryPolicy) -> core::result::Result<(), mxaccess::Error> pub fn mxaccess::Session::recovery_events(&self) -> tokio::sync::broadcast::Receiver> diff --git a/rust/crates/mxaccess-codec/benches/alloc_count.rs b/rust/crates/mxaccess-codec/benches/alloc_count.rs index ac5917c..17963bf 100644 --- a/rust/crates/mxaccess-codec/benches/alloc_count.rs +++ b/rust/crates/mxaccess-codec/benches/alloc_count.rs @@ -39,8 +39,7 @@ use std::alloc::{GlobalAlloc, Layout, System}; use std::sync::atomic::{AtomicU64, Ordering}; use mxaccess_codec::{ - MxReferenceHandle, NmxSubscriptionMessage, write_message, - write_message::WriteValue, + MxReferenceHandle, NmxSubscriptionMessage, write_message, write_message::WriteValue, }; // ---- counting allocator ------------------------------------------------- @@ -218,19 +217,9 @@ fn bench_subscription_decode() -> Row { fn bench_handle_from_names() -> Row { measure("MxReferenceHandle::from_names", 10_000, || { - let h = MxReferenceHandle::from_names( - 0, - 1, - 2, - 3, - "TestChildObject", - 0, - 1, - 0, - "TestInt", - false, - ) - .unwrap(); + let h = + MxReferenceHandle::from_names(0, 1, 2, 3, "TestChildObject", 0, 1, 0, "TestInt", false) + .unwrap(); std::hint::black_box(h); }) } diff --git a/rust/crates/mxaccess-codec/src/operation_status.rs b/rust/crates/mxaccess-codec/src/operation_status.rs index bc305d5..f422c96 100644 --- a/rust/crates/mxaccess-codec/src/operation_status.rs +++ b/rust/crates/mxaccess-codec/src/operation_status.rs @@ -21,11 +21,33 @@ //! [`NmxOperationStatusMessage::try_parse_inner`] is provided here. When //! `NmxObservedEnvelope` lands, add `try_parse_process_data_received_body` as //! a thin wrapper. +//! +//! ## Typed promotion and the synthesizer kernel +//! +//! [`NmxOperationStatusMessage::promote_to_typed`] returns the same +//! [`MxStatus`] the parser already attached to the message — the +//! verbatim-preserve placeholder for unknown shapes, the +//! [`MxStatus::WRITE_COMPLETE_OK`] sentinel for the proven +//! `(status_code=0x8050, completion_code=0x00)` shape. The 5-byte +//! `00 00 SS SS CC` inner body is **not** the same wire field as the +//! 4-byte packed status word `Lmx.dll!FUN_10100ce0` decodes +//! ([`MxStatus::from_packed_u32`]) — that kernel applies one layer up, +//! to the `INmxService.GetResponse2` payload's `status: i32` field +//! (carried e.g. in subscription records). See +//! `analysis/ghidra/exports/Lmx.dll.synthesizer-helpers2-decompile.md` +//! and `design/70-risks-and-open-questions.md` R3/R4 Path A for the +//! evidence chain. +//! +//! `promote_to_typed` is therefore a thin convenience over the existing +//! `status` field: callers that want the canonical bit-layout decoder +//! should reach for [`MxStatus::from_packed_u32`] directly when they +//! have a 4-byte packed value in hand. // Direct byte indexing — see reference_handle.rs for rationale. #![allow(clippy::indexing_slicing)] use crate::error::CodecError; +use crate::observed_frame::NmxObservedEnvelope; use crate::status::{MxStatus, MxStatusCategory, MxStatusSource}; /// Which of the two recognised inner-frame shapes was decoded @@ -78,6 +100,47 @@ impl NmxOperationStatusMessage { && self.completion_code == 0x00 } + /// Return the typed [`MxStatus`] for this frame. + /// + /// This is a thin convenience over [`Self::status`] — same value, + /// no transformation. Provided for API symmetry with + /// [`MxStatus::from_packed_u32`] (the canonical 4-byte synthesizer + /// kernel) and to give consumers a single entry point that can + /// be extended in future revisions if new evidence pins additional + /// `(status_code, completion_code)` shapes. + /// + /// **What this method does NOT do:** apply the + /// `Lmx.dll!FUN_10100ce0` synthesizer to the 5-byte inner body. + /// The 5-byte `00 00 SS SS CC` shape and the 4-byte packed-u32 + /// shape are different wire fields at different layers — see the + /// module docs and + /// `design/70-risks-and-open-questions.md` R3/R4 Path A. Callers + /// holding a 4-byte packed `MxStatus` (e.g. extracted from a + /// subscription record's `status: i32`) should call + /// [`MxStatus::from_packed_u32`] directly. + #[must_use] + pub const fn promote_to_typed(&self) -> MxStatus { + self.status + } + + /// Peel the outer [`NmxObservedEnvelope`] off a `ProcessDataReceived` + /// payload and parse the inner body. Mirrors + /// `NmxOperationStatusMessage.TryParseProcessDataReceivedBody` + /// (`NmxOperationStatusMessage.cs:20-32`). + /// + /// # Errors + /// + /// Returns `Err` when the outer envelope cannot be parsed or the + /// inner body matches no recognised shape (1- or 5-byte completion + /// frame). The .NET reference returns `false` and a `null!` + /// out-param in both cases; the Rust port surfaces a typed + /// [`CodecError`] so callers can distinguish "not a process-data + /// frame" from "successfully parsed". + pub fn try_parse_process_data_received_body(body: &[u8]) -> Result { + let envelope = NmxObservedEnvelope::parse_process_data_received_body_flexible(body)?; + Self::try_parse_inner(&envelope.inner_body) + } + /// Parse an inner body — either 1 byte (`CompletionOnly`) or 5 bytes /// (`StatusWord` with leading `00 00`). /// @@ -281,4 +344,38 @@ mod tests { let msg = NmxOperationStatusMessage::try_parse_inner(&frame).unwrap(); assert_eq!(msg.status_code, 0xBBAA); } + + #[test] + fn promote_to_typed_returns_existing_status_for_status_word() { + // The proven shape — must keep returning the canonical sentinel. + let frame = [0x00, 0x00, 0x50, 0x80, 0x00]; + let msg = NmxOperationStatusMessage::try_parse_inner(&frame).unwrap(); + assert_eq!(msg.promote_to_typed(), MxStatus::WRITE_COMPLETE_OK); + assert_eq!(msg.promote_to_typed(), msg.status); + } + + #[test] + fn promote_to_typed_returns_verbatim_status_for_completion_only() { + // 1-byte frames: no synthesizer evidence — must stay verbatim. + for byte in [0x00_u8, 0x41, 0xEF] { + let msg = NmxOperationStatusMessage::try_parse_inner(&[byte]).unwrap(); + let promoted = msg.promote_to_typed(); + assert_eq!(promoted, msg.status); + assert_eq!(promoted.category, MxStatusCategory::Unknown); + assert_eq!(promoted.detected_by, MxStatusSource::Unknown); + assert_eq!(promoted.detail, i16::from(byte)); + } + } + + #[test] + fn promote_to_typed_does_not_change_existing_status_field() { + // promote_to_typed must not mutate the verbatim-preserve `status` + // field. This guards the byte-for-byte parity contract with the + // .NET reference. + let frame = [0x00, 0x00, 0x55, 0xAA, 0x33]; + let msg = NmxOperationStatusMessage::try_parse_inner(&frame).unwrap(); + let original_status = msg.status; + let _typed = msg.promote_to_typed(); + assert_eq!(msg.status, original_status); + } } diff --git a/rust/crates/mxaccess-codec/src/status.rs b/rust/crates/mxaccess-codec/src/status.rs index 982aca8..4e0e1e1 100644 --- a/rust/crates/mxaccess-codec/src/status.rs +++ b/rust/crates/mxaccess-codec/src/status.rs @@ -22,7 +22,7 @@ pub enum MxStatusCategory { } impl MxStatusCategory { - pub fn from_i16(value: i16) -> Self { + pub const fn from_i16(value: i16) -> Self { match value { 0 => Self::Ok, 1 => Self::Pending, @@ -37,7 +37,7 @@ impl MxStatusCategory { } } - pub fn to_i16(self) -> i16 { + pub const fn to_i16(self) -> i16 { self as i16 } } @@ -59,7 +59,7 @@ pub enum MxStatusSource { } impl MxStatusSource { - pub fn from_i16(value: i16) -> Self { + pub const fn from_i16(value: i16) -> Self { match value { 0 => Self::RequestingLmx, 1 => Self::RespondingLmx, @@ -71,7 +71,7 @@ impl MxStatusSource { } } - pub fn to_i16(self) -> i16 { + pub const fn to_i16(self) -> i16 { self as i16 } } @@ -85,6 +85,135 @@ pub struct MxStatus { } impl MxStatus { + /// Decode a 4-byte packed `MxStatus` word. + /// + /// Mirrors the canonical NMX wire-frame status decoder + /// `Lmx.dll!FUN_10100ce0` (see + /// `analysis/ghidra/exports/Lmx.dll.synthesizer-helpers2-decompile.md`). + /// That function reads 4 bytes from a stream into a u32 and unpacks + /// them via the bit layout: + /// + /// ```text + /// bit 31: success (-1 if set, 0 if clear) + /// bits 27..24: category (4 bits, masked by 0xF) + /// bits 23..20: detected_by (4 bits, masked by 0xF) + /// bits 15..0: detail (i16 — low 16 bits, signed) + /// bits 30..28, 19..16: reserved/padding (ignored) + /// ``` + /// + /// This is the **synthesizer kernel** documented in + /// `design/70-risks-and-open-questions.md` R3/R4 Path A. Every NMX + /// wire frame that carries a status word emits one of these 4-byte + /// packings; the consumer-side dispatch (retry counters, callback + /// fan-out) is layered on top of the decoded `MxStatus`, but the + /// decoder itself is byte-deterministic and context-free. + /// + /// The `success` field is normalized to either `0` or `-1` per the + /// native `Lmx.dll` semantics: any value with bit 31 set decodes to + /// `-1`, any value with bit 31 clear decodes to `0`. (Native code: + /// `*param_1 = -(ushort)(((uint)param_2 & 0x80000000) != 0)`.) + /// + /// Unknown category / detected_by codes (i.e. a 4-bit value that + /// does not match a documented [`MxStatusCategory`] / + /// [`MxStatusSource`] variant) decode to the corresponding + /// `Unknown` variant. The padding bits are silently discarded. + #[must_use] + pub const fn from_packed_u32(packed: u32) -> Self { + // Bit layout — see fn doc. + let success: i16 = if packed & 0x8000_0000 != 0 { -1 } else { 0 }; + let category_bits = ((packed >> 24) & 0xF) as i16; + let detected_by_bits = ((packed >> 20) & 0xF) as i16; + let detail = packed as i16; + Self { + success, + category: MxStatusCategory::from_i16(category_bits), + detected_by: MxStatusSource::from_i16(detected_by_bits), + detail, + } + } + + /// Construct an `MxStatus` from a single-byte NMX response code. + /// + /// Mirrors the synthesis switch in + /// `Lmx.dll!FUN_1010bd10` (`ScanOnDemandCallback::GetResponse`) + /// at lines 741-770 of + /// `analysis/ghidra/exports/Lmx.dll.synthesizer-decompile.md`. + /// When the NMX `responseCode` is non-zero (no payload status word + /// to parse), `Lmx.dll` constructs an `MxStatus` from the response + /// code itself using this fixed mapping: + /// + /// | responseCode | category | detected_by | + /// |---|---|---| + /// | `0x01`, `0x02` | `CommunicationError` | `RequestingNmx` | + /// | `0x03` | `ConfigurationError` | `RequestingNmx` | + /// | `0x04` | `ConfigurationError` | `RespondingNmx` | + /// | `0x05` | `CommunicationError` | `RespondingNmx` | + /// | `0x1A` | `CommunicationError` | `RequestingNmx` | + /// + /// `success` is `0` (not `-1`) and `detail` carries the response + /// code unchanged. Unmapped codes return `None` — the native code's + /// `default` branch leaves the synthesized status untouched, so the + /// caller falls back to a verbatim raw-byte placeholder per + /// `design/70-risks-and-open-questions.md` R3/R4. + /// + /// This is **not** the same wire field as the 1-byte completion + /// frames `0x00`/`0x41`/`0xEF` parsed by + /// [`crate::NmxOperationStatusMessage::try_parse_inner`]: those + /// live inside a `0x32`/`0x33` callback body, while this + /// `responseCode` is the second `out` parameter of + /// `INmxService.GetResponse2(...)` (one layer up the stack). + /// `Lmx.dll`'s decoder for the 1-byte completion frames does not + /// apply this synthesis. + #[must_use] + pub const fn from_nmx_response_code(response_code: u8) -> Option { + // Per `FUN_1010bd10:741-770` switch. + let (category, detected_by) = match response_code { + 0x01 | 0x02 => ( + MxStatusCategory::CommunicationError, + MxStatusSource::RequestingNmx, + ), + 0x03 => ( + MxStatusCategory::ConfigurationError, + MxStatusSource::RequestingNmx, + ), + 0x04 => ( + MxStatusCategory::ConfigurationError, + MxStatusSource::RespondingNmx, + ), + 0x05 => ( + MxStatusCategory::CommunicationError, + MxStatusSource::RespondingNmx, + ), + 0x1A => ( + MxStatusCategory::CommunicationError, + MxStatusSource::RequestingNmx, + ), + _ => return None, + }; + Some(Self { + success: 0, + category, + detected_by, + detail: response_code as i16, + }) + } + + /// Pack `self` back into the 4-byte NMX wire layout. Inverse of + /// [`Self::from_packed_u32`]. Useful for round-trip tests and + /// future encoder paths. + /// + /// Padding bits (30..28, 19..16) are emitted as zero. Bit 31 mirrors + /// `success != 0` — any non-zero `success` round-trips to `-1` + /// because the decoder normalizes to `0`/`-1` only. + #[must_use] + pub const fn to_packed_u32(self) -> u32 { + let success_bit: u32 = if self.success != 0 { 0x8000_0000 } else { 0 }; + let category_bits = ((self.category as i16) as u32 & 0xF) << 24; + let detected_by_bits = ((self.detected_by as i16) as u32 & 0xF) << 20; + let detail_bits = (self.detail as u16) as u32; + success_bit | category_bits | detected_by_bits | detail_bits + } + /// `(success=-1, Ok, RequestingLmx, detail=0)` — `MxStatus.DataChangeOk` /// from `MxStatus.cs:36-40`. pub const DATA_CHANGE_OK: Self = Self { @@ -311,4 +440,199 @@ mod tests { assert!(!MxStatus::SUSPEND_PENDING.is_ok()); assert!(!MxStatus::INVALID_REFERENCE_CONFIGURATION.is_ok()); } + + #[test] + fn from_packed_u32_zero_decodes_to_all_zeros() { + // packed=0 → success=0, category=Ok(0), detected_by=RequestingLmx(0), detail=0. + // The "all zeros" status is the simplest data-change-pending shape + // the wire can carry. + let s = MxStatus::from_packed_u32(0); + assert_eq!(s.success, 0); + assert_eq!(s.category, MxStatusCategory::Ok); + assert_eq!(s.detected_by, MxStatusSource::RequestingLmx); + assert_eq!(s.detail, 0); + } + + #[test] + fn from_packed_u32_high_bit_sets_success_to_negative_one() { + // Native: `*param_1 = -(ushort)(((uint)param_2 & 0x80000000) != 0)` + // For packed=0x80000000, success=-1, all other fields 0. + let s = MxStatus::from_packed_u32(0x8000_0000); + assert_eq!(s.success, -1); + assert_eq!(s.category, MxStatusCategory::Ok); + assert_eq!(s.detected_by, MxStatusSource::RequestingLmx); + assert_eq!(s.detail, 0); + } + + #[test] + fn from_packed_u32_decodes_data_change_ok_layout() { + // `MxStatus::DATA_CHANGE_OK` = (success=-1, Ok=0, RequestingLmx=0, + // detail=0). Pack: bit31=1, bits27..24=0, bits23..20=0, bits15..0=0. + // → 0x80000000. + let packed = MxStatus::DATA_CHANGE_OK.to_packed_u32(); + assert_eq!(packed, 0x8000_0000); + let round_trip = MxStatus::from_packed_u32(packed); + assert_eq!(round_trip, MxStatus::DATA_CHANGE_OK); + } + + #[test] + fn from_packed_u32_decodes_write_complete_ok_layout() { + // `MxStatus::WRITE_COMPLETE_OK` = (success=-1, Ok=0, + // RespondingAutomationObject=5, detail=0). Pack: bit31=1, + // bits27..24=0 (Ok), bits23..20=5, bits15..0=0. + // → 0x80500000. + let expected_packed: u32 = 0x80_50_00_00; + let s = MxStatus::from_packed_u32(expected_packed); + assert_eq!(s, MxStatus::WRITE_COMPLETE_OK); + assert_eq!(MxStatus::WRITE_COMPLETE_OK.to_packed_u32(), expected_packed); + } + + #[test] + fn from_packed_u32_extracts_category_from_bits_24_to_27() { + // category=4 (ConfigurationError) at bits 24..27. + // → 0x04000000. + let s = MxStatus::from_packed_u32(0x0400_0000); + assert_eq!(s.category, MxStatusCategory::ConfigurationError); + assert_eq!(s.detected_by, MxStatusSource::RequestingLmx); + assert_eq!(s.detail, 0); + } + + #[test] + fn from_packed_u32_extracts_detected_by_from_bits_20_to_23() { + // detected_by=2 (RequestingNmx) at bits 20..23. + // → 0x00200000. + let s = MxStatus::from_packed_u32(0x0020_0000); + assert_eq!(s.category, MxStatusCategory::Ok); + assert_eq!(s.detected_by, MxStatusSource::RequestingNmx); + assert_eq!(s.detail, 0); + } + + #[test] + fn from_packed_u32_extracts_detail_as_signed_low_16_bits() { + // detail=21 ("Invalid reference") at bits 0..15. + // → 0x00000015. + let s = MxStatus::from_packed_u32(0x0000_0015); + assert_eq!(s.detail, 21); + assert_eq!(s.detail_text(), Some("Invalid reference")); + + // Negative detail — high bit of low-16 set: 0xFFFF → -1. + let s = MxStatus::from_packed_u32(0x0000_FFFF); + assert_eq!(s.detail, -1); + } + + #[test] + fn from_packed_u32_padding_bits_are_ignored() { + // Bits 30..28 and 19..16 are padding/reserved per `FUN_10100ce0`. + // Setting them should not affect any decoded field. + // bit 31: success + // bits 30..28: padding (0x70_00_00_00) + // bits 27..24: category + // bits 23..20: detected_by + // bits 19..16: padding (0x00_0F_00_00) + // bits 15..0: detail + // Padding-only mask: 0x70_00_00_00 | 0x00_0F_00_00 = 0x700F_0000. + let with_padding = MxStatus::from_packed_u32(0x700F_0000); + let without_padding = MxStatus::from_packed_u32(0x0000_0000); + assert_eq!(with_padding, without_padding); + } + + #[test] + fn from_packed_u32_unknown_category_decodes_to_unknown_variant() { + // Category bits = 0xF (not a defined variant). + // → 0x0F000000. + let s = MxStatus::from_packed_u32(0x0F00_0000); + assert_eq!(s.category, MxStatusCategory::Unknown); + } + + #[test] + fn from_packed_u32_unknown_detected_by_decodes_to_unknown_variant() { + // detected_by bits = 0xF (not a defined variant). + // → 0x00F00000. + let s = MxStatus::from_packed_u32(0x00F0_0000); + assert_eq!(s.detected_by, MxStatusSource::Unknown); + } + + #[test] + fn round_trip_canonical_sentinels() { + // Every canonical sentinel must round-trip through pack→decode. + for &expected in &[ + MxStatus::DATA_CHANGE_OK, + MxStatus::WRITE_COMPLETE_OK, + MxStatus::ACTIVATE_OK, + // SuspendPending: detail=0, success=-1, Pending=1, RequestingLmx=0. + // → 0x81000000. + MxStatus::SUSPEND_PENDING, + // InvalidReferenceConfiguration: success=0, ConfigError=4, + // RequestingLmx=0, detail=6. → 0x04000006. + MxStatus::INVALID_REFERENCE_CONFIGURATION, + ] { + let packed = expected.to_packed_u32(); + let round_trip = MxStatus::from_packed_u32(packed); + assert_eq!(round_trip, expected, "round-trip failed for {expected:?}"); + } + } + + #[test] + fn from_nmx_response_code_proven_mappings() { + // Per `FUN_1010bd10:741-770` switch. + // 0x01, 0x02 → CommunicationError + RequestingNmx + for code in [0x01_u8, 0x02] { + let s = MxStatus::from_nmx_response_code(code).unwrap(); + assert_eq!(s.success, 0); + assert_eq!(s.category, MxStatusCategory::CommunicationError); + assert_eq!(s.detected_by, MxStatusSource::RequestingNmx); + assert_eq!(s.detail, i16::from(code)); + } + + // 0x03 → ConfigurationError + RequestingNmx + let s = MxStatus::from_nmx_response_code(0x03).unwrap(); + assert_eq!(s.category, MxStatusCategory::ConfigurationError); + assert_eq!(s.detected_by, MxStatusSource::RequestingNmx); + assert_eq!(s.detail, 3); + + // 0x04 → ConfigurationError + RespondingNmx + let s = MxStatus::from_nmx_response_code(0x04).unwrap(); + assert_eq!(s.category, MxStatusCategory::ConfigurationError); + assert_eq!(s.detected_by, MxStatusSource::RespondingNmx); + assert_eq!(s.detail, 4); + + // 0x05 → CommunicationError + RespondingNmx + let s = MxStatus::from_nmx_response_code(0x05).unwrap(); + assert_eq!(s.category, MxStatusCategory::CommunicationError); + assert_eq!(s.detected_by, MxStatusSource::RespondingNmx); + assert_eq!(s.detail, 5); + + // 0x1A → CommunicationError + RequestingNmx + let s = MxStatus::from_nmx_response_code(0x1A).unwrap(); + assert_eq!(s.category, MxStatusCategory::CommunicationError); + assert_eq!(s.detected_by, MxStatusSource::RequestingNmx); + assert_eq!(s.detail, 0x1A); + } + + #[test] + fn from_nmx_response_code_unmapped_returns_none() { + // Codes outside the proven {1,2,3,4,5,0x1a} set return None — the + // native code falls through `default` and leaves the synthesized + // status untouched. Per `design/70-risks-and-open-questions.md` + // R3/R4 the consumer must preserve the raw byte verbatim. + for code in [0x00_u8, 0x06, 0x10, 0x19, 0x1B, 0x41, 0xEF, 0xFF] { + assert!( + MxStatus::from_nmx_response_code(code).is_none(), + "response code 0x{code:02X} should be unmapped" + ); + } + } + + #[test] + fn to_packed_u32_normalizes_arbitrary_success_to_high_bit_only() { + // The decoder produces `success ∈ {0, -1}`, so `to_packed_u32` + // only checks `success != 0` — the actual integer doesn't + // matter beyond zero/non-zero. + let mut s = MxStatus::DATA_CHANGE_OK; + s.success = 42; // Non-canonical value. + let packed = s.to_packed_u32(); + assert_eq!(packed & 0x8000_0000, 0x8000_0000); + // Round-trip normalizes to -1. + assert_eq!(MxStatus::from_packed_u32(packed).success, -1); + } } diff --git a/rust/crates/mxaccess/examples/asb-subscribe.rs b/rust/crates/mxaccess/examples/asb-subscribe.rs index 263d809..5d54b06 100644 --- a/rust/crates/mxaccess/examples/asb-subscribe.rs +++ b/rust/crates/mxaccess/examples/asb-subscribe.rs @@ -131,7 +131,9 @@ async fn main() -> Result<(), Box> { // -- Subscribe-flow ---------------------------------------------------- if env.run_subscribe { - eprintln!("creating subscription [canonical XML CreateSubscription] (max_queue=100, sample=1s)"); + eprintln!( + "creating subscription [canonical XML CreateSubscription] (max_queue=100, sample=1s)" + ); // SampleInterval is in **milliseconds** on the wire — the .NET // reference's `MxAsbDataClient.CreateSubscription` / // `AddMonitoredItems` default is `ulong sampleInterval = 1000` @@ -140,7 +142,10 @@ async fn main() -> Result<(), Box> { // poll would always come back empty. let sample_interval_ms: u64 = 1000; let max_queue_size: i64 = 100; - let sub_response = match client.create_subscription(max_queue_size, sample_interval_ms).await { + let sub_response = match client + .create_subscription(max_queue_size, sample_interval_ms) + .await + { Ok(r) => r, Err(e) => { eprintln!(" create_subscription failed: {e}"); @@ -165,11 +170,16 @@ async fn main() -> Result<(), Box> { )]; eprintln!("adding monitored items [canonical XML AddMonitoredItems]"); - let add = match client.add_monitored_items(sub_response.subscription_id, &monitored, true).await { + let add = match client + .add_monitored_items(sub_response.subscription_id, &monitored, true) + .await + { Ok(r) => r, Err(e) => { eprintln!(" add_monitored_items failed: {e}"); - let _ = client.delete_subscription(sub_response.subscription_id).await; + let _ = client + .delete_subscription(sub_response.subscription_id) + .await; eprintln!("disconnecting"); client.disconnect().await?; client.send_end().await?; @@ -184,17 +194,24 @@ async fn main() -> Result<(), Box> { add.status.first().map(|s| s.error_code).unwrap_or(0), ); - eprintln!("publishing [canonical XML Publish] (target {} polls × 5s)", env.subscribe_count); + eprintln!( + "publishing [canonical XML Publish] (target {} polls × 5s)", + env.subscribe_count + ); let mut total_values = 0usize; for poll in 0..env.subscribe_count { match tokio::time::timeout( Duration::from_secs(5), client.publish(sub_response.subscription_id), - ).await { + ) + .await + { Ok(Ok(resp)) => { eprintln!( " poll {poll}: {} value(s); result_code={:?} success={:?}", - resp.values.len(), resp.result_code, resp.success + resp.values.len(), + resp.result_code, + resp.success ); for v in &resp.values { total_values += 1; @@ -204,9 +221,7 @@ async fn main() -> Result<(), Box> { v.value.value ); } - if resp.result_code - == Some(mxaccess_asb::RESULT_CODE_INVALID_CONNECTION_ID) - { + if resp.result_code == Some(mxaccess_asb::RESULT_CODE_INVALID_CONNECTION_ID) { eprintln!(" publish surfaced InvalidConnectionId; bailing the loop"); break; } @@ -235,12 +250,18 @@ async fn main() -> Result<(), Box> { } // -- DeleteMonitoredItems / DeleteSubscription - if let Err(e) = client.delete_monitored_items(sub_response.subscription_id, &monitored).await { + if let Err(e) = client + .delete_monitored_items(sub_response.subscription_id, &monitored) + .await + { eprintln!("delete_monitored_items failed: {e}"); } else { eprintln!("delete_monitored_items ok [canonical XML DeleteMonitoredItems]"); } - if let Err(e) = client.delete_subscription(sub_response.subscription_id).await { + if let Err(e) = client + .delete_subscription(sub_response.subscription_id) + .await + { eprintln!("delete_subscription failed: {e}"); } else { eprintln!("delete_subscription ok [canonical XML DeleteSubscription]"); @@ -290,8 +311,12 @@ impl LiveEnv { let via_uri = std::env::var("MX_ASB_VIA").unwrap_or_else(|_| format!("net.tcp://{host}/ASBService")); let tag = std::env::var("MX_TEST_TAG").unwrap_or_else(|_| "TestChildObject.TestInt".into()); - let run_write = std::env::var("MX_RUN_WRITE").map(|v| v != "0").unwrap_or(true); - let run_subscribe = std::env::var("MX_RUN_SUBSCRIBE").map(|v| v != "0").unwrap_or(true); + let run_write = std::env::var("MX_RUN_WRITE") + .map(|v| v != "0") + .unwrap_or(true); + let run_subscribe = std::env::var("MX_RUN_SUBSCRIBE") + .map(|v| v != "0") + .unwrap_or(true); let subscribe_count = std::env::var("MX_SUBSCRIBE_COUNT") .ok() .and_then(|s| s.parse().ok()) diff --git a/rust/crates/mxaccess/src/asb_session.rs b/rust/crates/mxaccess/src/asb_session.rs index 84464ff..dab7123 100644 --- a/rust/crates/mxaccess/src/asb_session.rs +++ b/rust/crates/mxaccess/src/asb_session.rs @@ -60,7 +60,7 @@ use mxaccess_asb::{ }; use mxaccess_asb_nettcp::auth::CryptoParameters; use tokio::net::TcpStream; -use tokio::sync::{mpsc, Mutex}; +use tokio::sync::{Mutex, mpsc}; use tokio::task::JoinHandle; use tokio_stream::wrappers::ReceiverStream; @@ -410,17 +410,13 @@ async fn publish_loop( // on every Publish poll while values are still // delivered, so blanket "bail on any non-zero" // (the original F33 fix) was too aggressive. - if response.result_code - == Some(mxaccess_asb::RESULT_CODE_INVALID_CONNECTION_ID) - { + if response.result_code == Some(mxaccess_asb::RESULT_CODE_INVALID_CONNECTION_ID) { let _ = tx - .send(Err(Error::Connection( - ConnectionError::TransportFailure { - detail: "publish returned InvalidConnectionId — \ + .send(Err(Error::Connection(ConnectionError::TransportFailure { + detail: "publish returned InvalidConnectionId — \ session desynced, terminating stream" - .to_string(), - }, - ))) + .to_string(), + }))) .await; return; } @@ -609,7 +605,13 @@ mod tests { let calls_clone = calls.clone(); let publish_fn = move || { calls_clone.fetch_add(1, Ordering::Relaxed); - async move { Ok(fake_response(vec![fake_value(7), fake_value(8), fake_value(9)])) } + async move { + Ok(fake_response(vec![ + fake_value(7), + fake_value(8), + fake_value(9), + ])) + } }; // Drop the receiver immediately — first send triggers exit. drop(rx); diff --git a/rust/crates/mxaccess/src/lib.rs b/rust/crates/mxaccess/src/lib.rs index bd0ddc4..21d8a89 100644 --- a/rust/crates/mxaccess/src/lib.rs +++ b/rust/crates/mxaccess/src/lib.rs @@ -39,7 +39,7 @@ pub use transport_asb::AsbTransport; pub use mxaccess_galaxy::{GalaxyTagMetadata, Resolver, ResolverError}; pub use mxaccess_nmx::WriteValue; -pub use session::{RebuildFactory, Subscription}; +pub use session::{OperationContext, OperationKind, OperationStatus, RebuildFactory, Subscription}; /// Async session façade. Cheap clones share the inner state; drop of the last /// clone fires `UnregisterEngine` best-effort. For deterministic shutdown, @@ -391,7 +391,9 @@ pub enum ConfigError { /// `Session::recover_connection` was called without a /// [`crate::RebuildFactory`] installed via /// [`crate::Session::set_recovery_factory`]. F16. - #[error("recover_connection: no rebuild factory installed (call Session::set_recovery_factory)")] + #[error( + "recover_connection: no rebuild factory installed (call Session::set_recovery_factory)" + )] RecoveryNotConfigured, } diff --git a/rust/crates/mxaccess/src/session.rs b/rust/crates/mxaccess/src/session.rs index 25fb423..c7c8055 100644 --- a/rust/crates/mxaccess/src/session.rs +++ b/rust/crates/mxaccess/src/session.rs @@ -34,7 +34,8 @@ use std::time::SystemTime; use mxaccess_callback::{CallbackEvent, CallbackExporter, ExporterIdentities}; use mxaccess_codec::{ - MxStatus, NmxReferenceRegistrationMessage, NmxSubscriptionMessage, NmxSubscriptionRecord, + MxStatus, NmxOperationStatusMessage, NmxReferenceRegistrationMessage, NmxSubscriptionMessage, + NmxSubscriptionRecord, }; use mxaccess_galaxy::{GalaxyTagMetadata, Resolver, ResolverError}; use mxaccess_nmx::{NmxClient, NmxClientError, WriteValue}; @@ -51,7 +52,7 @@ use tokio_stream::wrappers::BroadcastStream; use crate::metrics as session_metrics; use crate::{DataChange, RecoveryEvent}; -use futures_util::Stream; +use futures_util::{Stream, StreamExt}; /// Capacity of the broadcast channel that fans out /// [`RecoveryEvent`]s to consumers via [`Session::recovery_events`]. @@ -77,6 +78,124 @@ use crate::{ /// either keep up or accept lag-loss. const CALLBACK_BROADCAST_CAPACITY: usize = 256; +/// Capacity of the broadcast channel that fans out parsed +/// [`OperationStatus`] events to consumers via +/// [`Session::operation_status_events`]. +/// +/// Operation-status frames are bursty (one per write completion / one +/// per subscription state change) but lower-volume than data updates. +/// Picked to absorb a short burst without dropping for a briefly slow +/// consumer. +const OPERATION_STATUS_BROADCAST_CAPACITY: usize = 64; + +/// Operation kind associated with an outstanding RPC. Mirrors the +/// distinct request paths the .NET reference tracks across +/// `MxNativeSession.{WriteAsync, WriteSecuredAsync, ReadAsync, +/// SubscribeAsync, UnsubscribeAsync, ActivateAsync, SuspendAsync}`. +/// +/// The Rust port uses this to enrich [`OperationStatus`] events with +/// the originating call's intent — the synthesizer kernel +/// ([`MxStatus::from_packed_u32`]) is byte-deterministic and does NOT +/// depend on `OperationKind`, but consumers often want to filter +/// "write completions" from "subscription state changes" without +/// peeking at the raw frame bytes. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum OperationKind { + /// Plain `Write` (`MxNativeSession.WriteAsync`). + Write, + /// `WriteSecured` / `WriteSecured2` (two-token writes; see R6). + WriteSecured, + /// `Read` (read-as-subscribe pattern at `cs:312-359`). + Read, + /// `Subscribe` / `AdviseSupervisory` / `RegisterReference`. + Subscribe, + /// `Unsubscribe` / `UnAdvise`. + Unsubscribe, + /// `Activate` (re-enable a suspended subscription). + Activate, + /// `Suspend` (pause an active subscription). + Suspend, + /// Operation kind unknown to the Rust port — surfaced as a + /// fallback when the originating call doesn't fit a typed variant + /// (e.g. raw transport-level operations). + Other, +} + +/// Per-operation context tracked for outstanding RPCs. +/// +/// The Rust port currently uses this struct only to enrich +/// [`OperationStatus`] events surfaced via +/// [`Session::operation_status_events`]. Future work +/// (`design/70-risks-and-open-questions.md` R3/R4 Path A follow-on) +/// will let the consumer correlate completion frames back to specific +/// outstanding write/subscribe calls; the current bring-up always +/// emits `OperationStatus.context = None` because the operation→ +/// completion correlation channel is not yet wired. +/// +/// Mirrors the bookkeeping `MxNativeSession` does in its private +/// `_pendingWrites` / `_pendingReads` dictionaries (referenced +/// in the source but not exposed publicly). +#[derive(Debug, Clone)] +#[non_exhaustive] +pub struct OperationContext { + /// 16-byte correlation id the originating call generated. For + /// subscribe/unsubscribe this matches `Subscription::correlation_id`; + /// for write/read this is the request's `correlationId` field. + pub correlation_id: [u8; 16], + /// Intent of the originating operation — see [`OperationKind`]. + pub op_kind: OperationKind, + /// Reference string (`Object.Attribute`) the operation targets, + /// when known. `None` for operations that don't carry one (e.g. + /// session-level ops). + pub reference: Option>, + /// Retry counter — incremented each time the consumer re-issues + /// the operation (e.g. via `Session::recover_connection`'s + /// re-advise loop). Always `0` on the first attempt. + pub retry_count: u32, +} + +/// One operation-status event surfaced to consumers via +/// [`Session::operation_status_events`]. +/// +/// Mirrors `MxNativeOperationStatusEvent` (`MxNativeSession.cs:73-78`) +/// with the addition of typed [`MxStatus`] promotion (the +/// `Lmx.dll!FUN_10100ce0` synthesizer kernel — see +/// `design/70-risks-and-open-questions.md` R3/R4 Path A). +/// +/// - [`Self::raw`] preserves the parsed frame byte-for-byte (matching +/// the .NET `Message` field). +/// - [`Self::status`] is the typed `MxStatus`. For 5-byte status-word +/// frames this is the canonical sentinel +/// ([`MxStatus::WRITE_COMPLETE_OK`] for the proven `(0x8050, 0x00)` +/// shape) or the verbatim-preserve placeholder for unknown shapes. +/// For 1-byte completion frames this is the verbatim-preserve +/// placeholder per R3/R4. **Callers holding a 4-byte packed status +/// word from a different layer should call +/// [`MxStatus::from_packed_u32`] directly.** +/// - [`Self::context`] carries the originating +/// [`OperationContext`] when the event can be correlated back to a +/// tracked outstanding operation. The current implementation +/// always emits `None` — operation-tracking plumbing lands as a +/// follow-up (see the module-level docs). +#[derive(Debug, Clone)] +#[non_exhaustive] +pub struct OperationStatus { + /// Raw parsed frame, byte-for-byte preserved. + pub raw: NmxOperationStatusMessage, + /// Typed status (synthesizer-promoted for known shapes; verbatim + /// for unknown). + pub status: MxStatus, + /// Optional originating-call context. Always `None` until the + /// operation-tracking plumbing is wired (see module-level docs). + pub context: Option, + /// `true` when the frame arrived during an active + /// `Session::recover_connection` window. Mirrors + /// `MxNativeOperationStatusEvent.IsDuringRecovery` + /// (`MxNativeSession.cs:78`). + pub is_during_recovery: bool, +} + /// Subscription handle returned by [`Session::subscribe`]. Implements /// `Stream>` — driving it forward /// yields one [`DataChange`] per matching record observed on the @@ -336,6 +455,19 @@ pub struct SessionInner { /// Broadcast channel that fans out parsed callback messages. Tap /// via [`Session::callbacks`]. pub(crate) callback_tx: broadcast::Sender>, + /// Broadcast channel that fans out parsed operation-status events. + /// Tap via [`Session::operation_status_events`]. + pub(crate) operation_status_tx: broadcast::Sender>, + /// Atomic counter incremented by `recover_connection` while a + /// recovery attempt is in flight. The router reads this when + /// constructing `OperationStatus` events to populate + /// `is_during_recovery`. Mirrors `MxNativeSession._recoveryActive` + /// (`MxNativeSession.cs:573` — `Volatile.Read(ref _recoveryActive)`). + /// + /// Wrapped in `Arc` so the router task (spawned at session + /// bring-up) can observe flips from `recover_connection` without + /// holding a strong reference to the entire `SessionInner`. + pub(crate) recovery_active: Arc, /// Handle to the router task that drains the /// [`CallbackExporter`]'s `CallbackEvent` channel and pushes parsed /// `NmxSubscriptionMessage`s onto `callback_tx`. `None` after @@ -448,9 +580,8 @@ pub(crate) enum SubscriptionMode { pub type RebuildFactory = Arc< dyn Fn() -> std::pin::Pin< Box< - dyn std::future::Future< - Output = Result, - > + Send, + dyn std::future::Future> + + Send, >, > + Send + Sync, @@ -472,8 +603,9 @@ impl std::fmt::Debug for SessionInner { } } -/// Drain `CallbackExporter` events, decode `CallbackInvoked` bodies as -/// `NmxSubscriptionMessage`, and broadcast each parsed message. +/// Drain `CallbackExporter` events, decode `CallbackInvoked` bodies, +/// and broadcast typed messages onto `callback_tx` (subscription +/// callbacks) or `operation_status_tx` (operation-status frames). /// /// Exits when the upstream `CallbackEvent` channel closes (which /// happens when the `CallbackExporter` is dropped or @@ -482,17 +614,46 @@ impl std::fmt::Debug for SessionInner { /// need them can subscribe to the raw `CallbackExporter` events /// directly via a future "diagnostic-channel" hook (no followup yet /// — surface only when a real consumer asks). +/// +/// Dispatch order mirrors +/// `MxNativeSession.OnCallbackReceived` (`cs:571-607`): +/// operation-status first (the simplest 1- or 5-byte payload), then +/// fall through to subscription messages. The `is_during_recovery` +/// flag on each emitted [`OperationStatus`] is taken from the live +/// `recovery_active` counter so the receiver matches the .NET +/// reference's volatile-read semantics at `cs:573`. pub(crate) async fn callback_router( mut events: tokio::sync::mpsc::UnboundedReceiver, callback_tx: broadcast::Sender>, + operation_status_tx: broadcast::Sender>, + recovery_active: Arc, ) { while let Some(event) = events.recv().await { if let CallbackEvent::CallbackInvoked { body, .. } = event { - // The body is the inner NMX subscription message — same - // 23-byte preamble + records as `NmxSubscriptionMessage::parse_inner` - // expects. Parse failures are silent (no consumer) since the - // .NET reference also fires `UnparsedCallbackReceived` events - // separately and we don't model that yet. + // 1. Try operation-status first — peels the outer envelope + // and parses a 1- or 5-byte completion frame. Mirrors + // `MxNativeSession.OnCallbackReceived:574`. + if let Ok(op) = NmxOperationStatusMessage::try_parse_process_data_received_body(&body) { + let is_during_recovery = + recovery_active.load(std::sync::atomic::Ordering::Acquire) > 0; + let typed = op.promote_to_typed(); + let _ = operation_status_tx.send(Arc::new(OperationStatus { + raw: op, + status: typed, + // Operation-tracking plumbing not yet wired — + // always emit context=None for now (R3/R4 + // follow-on tracks adding the correlation channel). + context: None, + is_during_recovery, + })); + continue; + } + + // 2. Fall through to subscription messages — same 23-byte + // preamble + records as `NmxSubscriptionMessage::parse_inner` + // expects. Parse failures are silent (no consumer) since + // the .NET reference also fires `UnparsedCallbackReceived` + // events separately and we don't model that yet. if let Ok(msg) = NmxSubscriptionMessage::parse_inner(&body) { // `send` returns `Err(SendError)` only when there are zero // receivers — that's fine for this wire path; nothing to do. @@ -618,7 +779,15 @@ impl Session { // 2. Spawn the router task that broadcasts parsed callback // messages. let (callback_tx, _) = broadcast::channel(CALLBACK_BROADCAST_CAPACITY); - let router_handle = tokio::spawn(callback_router(callback_events, callback_tx.clone())); + let (operation_status_tx, _) = + broadcast::channel::>(OPERATION_STATUS_BROADCAST_CAPACITY); + let recovery_active = Arc::new(std::sync::atomic::AtomicU32::new(0)); + let router_handle = tokio::spawn(callback_router( + callback_events, + callback_tx.clone(), + operation_status_tx.clone(), + recovery_active.clone(), + )); // 3. RegisterEngine2 with the callback OBJREF. Mirrors cs:163-175. let hr = nmx @@ -662,6 +831,8 @@ impl Session { nmx: Mutex::new(nmx), callback_exporter: Mutex::new(Some(exporter)), callback_tx, + operation_status_tx, + recovery_active, router_handle: std::sync::Mutex::new(Some(router_handle)), recovery_tx, connected: std::sync::atomic::AtomicBool::new(true), @@ -689,6 +860,59 @@ impl Session { self.inner.recovery_tx.subscribe() } + /// Subscribe to operation-status events. + /// + /// Returns a [`broadcast::Receiver`] that yields one + /// [`OperationStatus`] per parsed completion frame. Mirrors + /// `MxNativeSession.OperationStatusReceived` + /// (`MxNativeSession.cs:118`) but exposes the typed + /// [`MxStatus`] (the synthesizer kernel + /// [`MxStatus::from_packed_u32`] is applied where the bit layout + /// matches; verbatim-preserve placeholders are returned for the + /// 1-byte completion frames per + /// `design/70-risks-and-open-questions.md` R3/R4). + /// + /// Slow consumers see `RecvError::Lagged(n)` from the underlying + /// broadcast — the wire protocol does not replay missed + /// operation-status frames so consumers must keep up or accept + /// lag-loss. + /// + /// The first emitted event will have + /// [`OperationStatus::context`] == `None` for now — + /// operation-tracking plumbing (correlating completion frames + /// back to outstanding writes/subscribes) is the next step in the + /// R3/R4 follow-on work. The synthesizer kernel itself is in place + /// today. + #[must_use] + pub fn operation_status_events(&self) -> broadcast::Receiver> { + self.inner.operation_status_tx.subscribe() + } + + /// Stream variant of [`Self::operation_status_events`]: yields + /// `Result, Error>` per item, mapping + /// broadcast lag to a typed error. + /// + /// Mirrors the `Stream`-based access pattern already provided by + /// the `Subscription::Stream` impl. Use the raw + /// [`broadcast::Receiver`] returned by + /// [`Self::operation_status_events`] when control over backpressure + /// or lag-handling matters. + pub fn operation_status_stream( + &self, + ) -> impl Stream, Error>> + Send { + let rx = self.inner.operation_status_tx.subscribe(); + BroadcastStream::new(rx).map(|item| match item { + Ok(ev) => Ok(ev), + Err(tokio_stream::wrappers::errors::BroadcastStreamRecvError::Lagged(n)) => { + Err(Error::Configuration(ConfigError::InvalidArgument { + detail: format!( + "operation-status stream lagged behind broadcast by {n} events" + ), + })) + } + }) + } + /// Install the [`RebuildFactory`] used by [`Self::recover_connection`] /// to build a fresh [`NmxClient`] on each retry attempt. Without /// a factory, `recover_connection` returns @@ -744,9 +968,26 @@ impl Session { // recovery body can take the nmx mutex without deadlocking. let factory = { let lock = self.inner.rebuild_factory.lock().await; - lock.clone().ok_or(Error::Configuration( - ConfigError::RecoveryNotConfigured, - ))? + lock.clone() + .ok_or(Error::Configuration(ConfigError::RecoveryNotConfigured))? + }; + + // Mark the session as in-recovery so the callback router + // stamps `OperationStatus.is_during_recovery = true` for any + // events that arrive during the attempt. Mirrors + // `MxNativeSession._recoveryActive` (`cs:573` — volatile + // increment around `RecoverConnectionCore`). + struct RecoveryGuard(Arc); + impl Drop for RecoveryGuard { + fn drop(&mut self) { + self.0.fetch_sub(1, std::sync::atomic::Ordering::Release); + } + } + let _recovery_guard = { + self.inner + .recovery_active + .fetch_add(1, std::sync::atomic::Ordering::AcqRel); + RecoveryGuard(self.inner.recovery_active.clone()) }; let mut last_error: Option = None; @@ -772,10 +1013,9 @@ impl Session { // `Error` doesn't impl `Clone` (the io::Error source isn't // cloneable), so capture a string copy for the bubbled-up // last_error and hand the original to the broadcast event. - let bubbled = - Error::Connection(ConnectionError::TransportFailure { - detail: e.to_string(), - }); + let bubbled = Error::Connection(ConnectionError::TransportFailure { + detail: e.to_string(), + }); let _ = self.inner.recovery_tx.send(Arc::new(RecoveryEvent::Failed { attempt, error: e, @@ -789,9 +1029,7 @@ impl Session { } } - Err(last_error.unwrap_or(Error::Connection( - ConnectionError::EngineNotRegistered, - ))) + Err(last_error.unwrap_or(Error::Connection(ConnectionError::EngineNotRegistered))) } /// Single-attempt body of [`Self::recover_connection`], split out so @@ -907,9 +1145,7 @@ impl Session { ) .map_err(|e| { Error::Configuration(ConfigError::InvalidArgument { - detail: format!( - "recovery: buffered item definition: {e}" - ), + detail: format!("recovery: buffered item definition: {e}"), }) })?; let registration = NmxReferenceRegistrationMessage { @@ -1308,13 +1544,14 @@ impl Session { // reference's split-context form is reachable via the // compat-server layer F35 once it lands). The codec helper // rejects empty/whitespace inputs with `CodecError::InvalidName`. - let item_definition = - NmxReferenceRegistrationMessage::to_buffered_item_definition(reference) - .map_err(|e| { - Error::Configuration(ConfigError::InvalidArgument { - detail: format!("buffered item definition: {e}"), - }) - })?; + let item_definition = NmxReferenceRegistrationMessage::to_buffered_item_definition( + reference, + ) + .map_err(|e| { + Error::Configuration(ConfigError::InvalidArgument { + detail: format!("buffered item definition: {e}"), + }) + })?; let registration = NmxReferenceRegistrationMessage { item_handle: 0, item_correlation_id: correlation_id, @@ -1876,7 +2113,15 @@ mod tests { .await .unwrap(); let (callback_tx, _) = broadcast::channel(CALLBACK_BROADCAST_CAPACITY); - let router_handle = tokio::spawn(callback_router(callback_events, callback_tx.clone())); + let (operation_status_tx, _) = + broadcast::channel::>(OPERATION_STATUS_BROADCAST_CAPACITY); + let recovery_active = Arc::new(std::sync::atomic::AtomicU32::new(0)); + let router_handle = tokio::spawn(callback_router( + callback_events, + callback_tx.clone(), + operation_status_tx.clone(), + recovery_active.clone(), + )); let (recovery_tx, _) = broadcast::channel(RECOVERY_BROADCAST_CAPACITY); Ok(Session { @@ -1886,6 +2131,8 @@ mod tests { nmx: Mutex::new(nmx), callback_exporter: Mutex::new(Some(exporter)), callback_tx, + operation_status_tx, + recovery_active, router_handle: std::sync::Mutex::new(Some(router_handle)), recovery_tx, connected: std::sync::atomic::AtomicBool::new(true), @@ -2323,8 +2570,15 @@ mod tests { // broadcast pair to test the routing logic in isolation. let (event_tx, event_rx) = tokio::sync::mpsc::unbounded_channel(); let (callback_tx, mut callback_rx) = broadcast::channel(8); + let (operation_status_tx, _) = broadcast::channel::>(8); + let recovery_active = Arc::new(std::sync::atomic::AtomicU32::new(0)); - let router_h = tokio::spawn(callback_router(event_rx, callback_tx)); + let router_h = tokio::spawn(callback_router( + event_rx, + callback_tx, + operation_status_tx, + recovery_active, + )); // Build a minimal valid 0x32 SubscriptionStatus body: 23-byte // preamble + 16-byte item_correlation_id, record_count=0 so no @@ -2578,9 +2832,9 @@ mod tests { let stub: crate::RebuildFactory = Arc::new(|| { Box::pin(async { Err(mxaccess_nmx::NmxClientError::Transport( - mxaccess_rpc::transport::TransportError::Io( - std::io::Error::other("synthetic rebuild failure"), - ), + mxaccess_rpc::transport::TransportError::Io(std::io::Error::other( + "synthetic rebuild failure", + )), )) }) }); @@ -2603,9 +2857,7 @@ mod tests { for _ in 0..expected_events { match &*rx.recv().await.unwrap() { RecoveryEvent::Started { .. } => started += 1, - RecoveryEvent::Failed { - will_retry, .. - } => { + RecoveryEvent::Failed { will_retry, .. } => { failed += 1; last_will_retry = Some(*will_retry); } @@ -2631,8 +2883,7 @@ mod tests { // F16: every successful subscribe() inserts into the // SubscriptionEntry registry; unsubscribe() removes it. // Recovery walks this registry to replay AdviseSupervisory. - let (addr, handle) = - unauthenticated_server(vec![(0, Vec::new()), (0, Vec::new())]).await; + let (addr, handle) = unauthenticated_server(vec![(0, Vec::new()), (0, Vec::new())]).await; let resolver: Arc = Arc::new(StaticResolver::new(&[( "TestObj.TestInt", sample_metadata(), @@ -2706,10 +2957,7 @@ mod tests { let pfc_object_uuid = (req_h.packet_flags & 0x80) != 0; let stub_offset = if pfc_object_uuid { 8 + 16 } else { 8 }; let stub = body[stub_offset..].to_vec(); - recorded_for_task - .lock() - .unwrap() - .push((opnum, stub)); + recorded_for_task.lock().unwrap().push((opnum, stub)); let mut stub_resp = Vec::new(); stub_resp.extend_from_slice(&OrpcThat::default().encode()); @@ -2940,9 +3188,9 @@ mod tests { let stub: crate::RebuildFactory = Arc::new(|| { Box::pin(async { Err(mxaccess_nmx::NmxClientError::Transport( - mxaccess_rpc::transport::TransportError::Io( - std::io::Error::other("stub factory: rebuild always fails"), - ), + mxaccess_rpc::transport::TransportError::Io(std::io::Error::other( + "stub factory: rebuild always fails", + )), )) }) }); @@ -2951,9 +3199,7 @@ mod tests { let mut rx_a = session.recovery_events(); let mut rx_b = session.recovery_events(); - let _ = session - .recover_connection(RecoveryPolicy::default()) - .await; + let _ = session.recover_connection(RecoveryPolicy::default()).await; // First event from each receiver is the same Started Arc. let a = rx_a.recv().await.unwrap(); @@ -3071,6 +3317,181 @@ mod tests { handle.abort(); } + /// Build a `ProcessDataReceived`-style envelope wrapping a 5-byte + /// operation-status inner body. Mirrors what `NmxObservedEnvelope` + /// serialises (`mxaccess-codec/src/observed_frame.rs:115-141`): + /// + /// - 4-byte total-length prefix + /// - 46-byte header with `inner_length` at offset 6 (header + /// offset 4 + INNER_LENGTH_OFFSET 2) + /// - inner body + fn wrap_op_status_envelope(inner: &[u8]) -> Vec { + const HEADER_LENGTH: usize = 46; + let total_len = 4 + HEADER_LENGTH + inner.len(); + let mut body = vec![0u8; total_len]; + // Total-length prefix at offset 0. + body[0..4].copy_from_slice(&(total_len as i32).to_le_bytes()); + // `actualInnerLength = declaredInnerLength - sizeof(int)` per + // the parser at `observed_frame.rs:134`. So + // `declaredInnerLength = inner.len() + 4`. + let declared_inner: i32 = inner.len() as i32 + 4; + // Inner-length field sits at headerOffset + INNER_LENGTH_OFFSET + // = 4 + 2 = 6. + body[6..10].copy_from_slice(&declared_inner.to_le_bytes()); + // Inner body follows the header. + body[4 + HEADER_LENGTH..].copy_from_slice(inner); + body + } + + #[tokio::test] + async fn router_dispatches_status_word_frame_to_operation_status_channel() { + // End-to-end: hand-build an operation-status `ProcessDataReceived` + // body and confirm the router parses it + broadcasts an + // `OperationStatus` (NOT a subscription message). + let (event_tx, event_rx) = tokio::sync::mpsc::unbounded_channel(); + let (callback_tx, mut callback_rx) = broadcast::channel(8); + let (operation_status_tx, mut operation_status_rx) = + broadcast::channel::>(8); + let recovery_active = Arc::new(std::sync::atomic::AtomicU32::new(0)); + + let router_h = tokio::spawn(callback_router( + event_rx, + callback_tx, + operation_status_tx, + recovery_active, + )); + + // Inner body is the proven `00 00 50 80 00` 5-byte status-word frame. + let inner = [0x00, 0x00, 0x50, 0x80, 0x00]; + let body = wrap_op_status_envelope(&inner); + event_tx + .send(CallbackEvent::CallbackInvoked { opnum: 4, body }) + .unwrap(); + + let received = tokio::time::timeout( + std::time::Duration::from_secs(1), + operation_status_rx.recv(), + ) + .await + .expect("router timed out"); + let event = received.expect("broadcast recv error"); + + // Raw frame round-trips byte-exact. + assert_eq!(event.raw.command, 0x00); + assert_eq!(event.raw.status_code, 0x8050); + assert_eq!(event.raw.completion_code, 0x00); + + // Synthesizer-promoted status equals the canonical sentinel. + assert_eq!(event.status, MxStatus::WRITE_COMPLETE_OK); + + // Context not yet wired — always None for this iteration. + assert!(event.context.is_none()); + // No recovery in flight when the event was dispatched. + assert!(!event.is_during_recovery); + + // Subscription channel must NOT have received anything — the + // dispatcher's `continue` after operation-status hit means + // subscription parsing never runs for this body. + let cb_res = + tokio::time::timeout(std::time::Duration::from_millis(100), callback_rx.recv()).await; + assert!( + cb_res.is_err(), + "subscription channel got an unexpected event" + ); + + drop(event_tx); + let _ = router_h.await; + } + + #[tokio::test] + async fn router_dispatches_completion_only_frames_under_each_proven_byte() { + // Per `design/70-risks-and-open-questions.md` R3/R4 the three + // observed completion bytes are 0x00, 0x41, 0xEF. The synthesizer + // does NOT promote them (no upstream evidence per Path A's + // Ghidra walk); they should arrive on the operation-status + // channel as verbatim-preserve placeholders. + for byte in [0x00_u8, 0x41, 0xEF] { + let (event_tx, event_rx) = tokio::sync::mpsc::unbounded_channel(); + let (callback_tx, _callback_rx) = broadcast::channel(8); + let (operation_status_tx, mut operation_status_rx) = + broadcast::channel::>(8); + let recovery_active = Arc::new(std::sync::atomic::AtomicU32::new(0)); + let router_h = tokio::spawn(callback_router( + event_rx, + callback_tx, + operation_status_tx, + recovery_active, + )); + + let inner = [byte]; + let body = wrap_op_status_envelope(&inner); + event_tx + .send(CallbackEvent::CallbackInvoked { opnum: 4, body }) + .unwrap(); + + let received = tokio::time::timeout( + std::time::Duration::from_secs(1), + operation_status_rx.recv(), + ) + .await + .expect("router timed out"); + let event = received.expect("broadcast recv error"); + + assert_eq!(event.raw.completion_code, byte); + assert_eq!(event.status.detail, i16::from(byte)); + // R3/R4: completion-only bytes stay verbatim (Unknown/Unknown). + assert_eq!( + event.status.category, + mxaccess_codec::MxStatusCategory::Unknown + ); + assert_eq!( + event.status.detected_by, + mxaccess_codec::MxStatusSource::Unknown + ); + + drop(event_tx); + let _ = router_h.await; + } + } + + #[tokio::test] + async fn router_marks_is_during_recovery_when_counter_nonzero() { + // Stamp `recovery_active = 1` BEFORE feeding an event — the + // router should observe the volatile load and emit + // `OperationStatus.is_during_recovery = true`. Mirrors + // `MxNativeSession.OnCallbackReceived:573` which reads the same + // flag via `Volatile.Read(ref _recoveryActive)`. + let (event_tx, event_rx) = tokio::sync::mpsc::unbounded_channel(); + let (callback_tx, _callback_rx) = broadcast::channel(8); + let (operation_status_tx, mut operation_status_rx) = + broadcast::channel::>(8); + let recovery_active = Arc::new(std::sync::atomic::AtomicU32::new(1)); + let router_h = tokio::spawn(callback_router( + event_rx, + callback_tx, + operation_status_tx, + recovery_active, + )); + + let inner = [0x00, 0x00, 0x50, 0x80, 0x00]; + let body = wrap_op_status_envelope(&inner); + event_tx + .send(CallbackEvent::CallbackInvoked { opnum: 4, body }) + .unwrap(); + + let event = tokio::time::timeout( + std::time::Duration::from_secs(1), + operation_status_rx.recv(), + ) + .await + .expect("router timed out") + .expect("broadcast recv error"); + assert!(event.is_during_recovery); + + drop(event_tx); + let _ = router_h.await; + } + #[test] fn filetime_to_system_time_round_trip() { // Build a SystemTime, convert to FILETIME, convert back. @@ -3098,7 +3519,14 @@ mod tests { // window. let (event_tx, event_rx) = tokio::sync::mpsc::unbounded_channel(); let (callback_tx, mut callback_rx) = broadcast::channel(8); - let router_h = tokio::spawn(callback_router(event_rx, callback_tx)); + let (operation_status_tx, _) = broadcast::channel::>(8); + let recovery_active = Arc::new(std::sync::atomic::AtomicU32::new(0)); + let router_h = tokio::spawn(callback_router( + event_rx, + callback_tx, + operation_status_tx, + recovery_active, + )); event_tx .send(CallbackEvent::Bind { @@ -3142,7 +3570,11 @@ mod tests { // Issue a plain subscribe — server records AdviseSupervisory. let sub = session.subscribe("TestObj.TestInt").await.unwrap(); let cid = sub.correlation_id; - assert_eq!(recorded.lock().unwrap().len(), 1, "subscribe should issue 1 RPC"); + assert_eq!( + recorded.lock().unwrap().len(), + 1, + "subscribe should issue 1 RPC" + ); // Mutate the registry entry's mode to Buffered (synthesise the // state subscribe_buffered_nmx would have produced).