Decode GPS route element records
This commit is contained in:
@@ -166,13 +166,11 @@ Latest GPS-query finding:
|
|||||||
this is HUD/display lifecycle polling rather than solve completion: it polls
|
this is HUD/display lifecycle polling rather than solve completion: it polls
|
||||||
the currently displayed query once per second and pauses when the minimap is
|
the currently displayed query once per second and pauses when the minimap is
|
||||||
not rendered.
|
not rendered.
|
||||||
- The current installed probe adds a read-only dump of suspected result-object
|
- The current installed probe adds a read-only dump of result-object fields
|
||||||
fields around offsets `0x20` through `0x5f` on successful active-route
|
around offsets `0x20` through `0x5f` on successful active-route fetches.
|
||||||
fetches. The field at `outPath + 0x28` is confirmed to be a pointer-sized
|
The field at `outPath + 0x28` is the data pointer for an internal route
|
||||||
route-result member with a count at `outPath + 0x30`, but live data does not
|
element vector, but live data does not decode as plain `Vector3`/`Vector4`
|
||||||
decode as plain `Vector3`/`Vector4` coordinates. The next probe treats it as
|
coordinates.
|
||||||
an internal route element array and dumps raw bytes instead of assuming a
|
|
||||||
float-vector layout.
|
|
||||||
|
|
||||||
Latest route-result findings:
|
Latest route-result findings:
|
||||||
|
|
||||||
@@ -185,16 +183,19 @@ Latest route-result findings:
|
|||||||
result counts: side job `12`, Sinnerman `23`, Claire's Garage `18`, and the
|
result counts: side job `12`, Sinnerman `23`, Claire's Garage `18`, and the
|
||||||
custom pin `17`.
|
custom pin `17`.
|
||||||
- Static disassembly of the `0x520783` result-drain caller shows `0x7094b8`
|
- Static disassembly of the `0x520783` result-drain caller shows `0x7094b8`
|
||||||
drains one 0x68-byte route result record at a time from an internal vector.
|
drains one route result record at a time from an internal vector, then copies
|
||||||
- Inside that 0x68-byte record, `0x41c508` iterates the member at offset
|
that record into `outPath`.
|
||||||
`0x28` as an array of 8-byte pointers, using the dword at `+0x34` as the
|
- The copied route-result subobject contains a vector at `outPath + 0x28`.
|
||||||
live count. The dword at `+0x30` behaves like capacity or retained allocation
|
Its live count is the dword at `outPath + 0x34`; in the latest run this
|
||||||
size; earlier logs that treated `+0x30` as the point count were reading the
|
cleanly matched the visible routes: side job `12`, Sinnerman `23`, Claire's
|
||||||
right magnitude only while capacity and count matched.
|
Garage `18`, and custom pin `17`.
|
||||||
- The pointed objects appear to be route/lane segment objects rather than
|
- The vector elements are 0x28-byte records, not pointers. `0x41be2c` calls
|
||||||
coordinate vectors. The next probe logs the raw pointer array and selected
|
`0x41be94` with `(data, data + count * 40)`, so the next probe logs each
|
||||||
fields the engine reads from those pointed objects (`+0x68`, `+0x74`,
|
route element as a compact 40-byte record tuple:
|
||||||
`+0x90`, and `+0x98`).
|
`h00,u08,u0c,u10,u14,h18,h20`.
|
||||||
|
- A nearby helper at `0x41c508` does iterate an array of 8-byte pointers at
|
||||||
|
offset `+0x28`, but that is a different route-adjacent object type, not the
|
||||||
|
`0x7094b8` output record captured in `outPath`.
|
||||||
|
|
||||||
Autodrive finding:
|
Autodrive finding:
|
||||||
|
|
||||||
|
|||||||
@@ -908,42 +908,51 @@ void AppendGpsResultElementArraySummary(std::ostringstream& aLine, const char* a
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto rawSize = std::min<size_t>(0x80, static_cast<size_t>(count) * sizeof(void*));
|
constexpr uintptr_t kRouteRecordStride = 0x28;
|
||||||
AppendBytesPointer(aLine, (prefix + "_ptr28_ptrsRaw").c_str(), data, rawSize);
|
const auto rawSize = std::min<size_t>(0xA0, static_cast<size_t>(count) * kRouteRecordStride);
|
||||||
|
AppendBytesPointer(aLine, (prefix + "_ptr28_rec40raw").c_str(), data, rawSize);
|
||||||
|
|
||||||
const auto appendElement = [&](uint32_t aIndex, const char* aLabel) {
|
const auto flags = aLine.flags();
|
||||||
auto* element = ReadPointerField(data, static_cast<uintptr_t>(aIndex) * sizeof(void*));
|
const auto fill = aLine.fill();
|
||||||
const auto elementPrefix = prefix + "_ptr28_" + aLabel;
|
aLine << " " << prefix << "_ptr28_rec40=[";
|
||||||
AppendPointerWithRva(aLine, elementPrefix.c_str(), element);
|
for (uint32_t index = 0; index < count; ++index)
|
||||||
if (!element || reinterpret_cast<uintptr_t>(element) < 0x10000)
|
{
|
||||||
|
const auto recordAddress = reinterpret_cast<uintptr_t>(data) + static_cast<uintptr_t>(index) * kRouteRecordStride;
|
||||||
|
const auto* record = reinterpret_cast<const void*>(recordAddress);
|
||||||
|
|
||||||
|
uint64_t h00 = 0;
|
||||||
|
uint32_t u08 = 0;
|
||||||
|
uint32_t u0c = 0;
|
||||||
|
uint32_t u10 = 0;
|
||||||
|
uint32_t u14 = 0;
|
||||||
|
uint64_t h18 = 0;
|
||||||
|
uint64_t h20 = 0;
|
||||||
|
const auto hasRecord = TryReadMemory(record, h00) &&
|
||||||
|
TryReadMemory(reinterpret_cast<const void*>(recordAddress + 0x08), u08) &&
|
||||||
|
TryReadMemory(reinterpret_cast<const void*>(recordAddress + 0x0C), u0c) &&
|
||||||
|
TryReadMemory(reinterpret_cast<const void*>(recordAddress + 0x10), u10) &&
|
||||||
|
TryReadMemory(reinterpret_cast<const void*>(recordAddress + 0x14), u14) &&
|
||||||
|
TryReadMemory(reinterpret_cast<const void*>(recordAddress + 0x18), h18) &&
|
||||||
|
TryReadMemory(reinterpret_cast<const void*>(recordAddress + 0x20), h20);
|
||||||
|
|
||||||
|
if (index > 0)
|
||||||
{
|
{
|
||||||
return;
|
aLine << ";";
|
||||||
}
|
}
|
||||||
|
|
||||||
AppendPointerField(aLine, (elementPrefix + "_f68").c_str(), element, 0x68);
|
aLine << index << ":";
|
||||||
AppendUint32Field(aLine, (elementPrefix + "_u74").c_str(), element, 0x74);
|
if (!hasRecord)
|
||||||
AppendPointerField(aLine, (elementPrefix + "_f90").c_str(), element, 0x90);
|
|
||||||
AppendUint32Field(aLine, (elementPrefix + "_u98").c_str(), element, 0x98);
|
|
||||||
|
|
||||||
uint32_t sampleCount = 0;
|
|
||||||
auto* samples = ReadPointerField(element, 0x68);
|
|
||||||
TryReadMemory(reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(element) + 0x74), sampleCount);
|
|
||||||
if (samples && reinterpret_cast<uintptr_t>(samples) >= 0x10000 && sampleCount > 0 && sampleCount <= 4096)
|
|
||||||
{
|
{
|
||||||
const auto sampleRawSize = std::min<size_t>(0x20, static_cast<size_t>(sampleCount) * 0x08);
|
aLine << "<unreadable>";
|
||||||
AppendBytesPointer(aLine, (elementPrefix + "_f68raw").c_str(), samples, sampleRawSize);
|
continue;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const auto firstLimit = std::min<uint32_t>(count, 4);
|
aLine << std::hex << h00 << "," << std::dec << u08 << "," << u0c << "," << u10 << "," << u14 << ","
|
||||||
for (uint32_t index = 0; index < firstLimit; ++index)
|
<< std::hex << h18 << "," << h20 << std::dec;
|
||||||
{
|
|
||||||
appendElement(index, ("elem" + std::to_string(index)).c_str());
|
|
||||||
}
|
|
||||||
if (count > firstLimit)
|
|
||||||
{
|
|
||||||
appendElement(count - 1, "last");
|
|
||||||
}
|
}
|
||||||
|
aLine << "]";
|
||||||
|
aLine.flags(flags);
|
||||||
|
aLine.fill(fill);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppendGpsResultObjectSummary(std::ostringstream& aLine, const char* aPrefix, void* aResultObject)
|
void AppendGpsResultObjectSummary(std::ostringstream& aLine, const char* aPrefix, void* aResultObject)
|
||||||
@@ -958,8 +967,6 @@ void AppendGpsResultObjectSummary(std::ostringstream& aLine, const char* aPrefix
|
|||||||
AppendUint32Field(aLine, (std::string(aPrefix) + "_u20").c_str(), aResultObject, 0x20);
|
AppendUint32Field(aLine, (std::string(aPrefix) + "_u20").c_str(), aResultObject, 0x20);
|
||||||
AppendUint32Field(aLine, (std::string(aPrefix) + "_u24").c_str(), aResultObject, 0x24);
|
AppendUint32Field(aLine, (std::string(aPrefix) + "_u24").c_str(), aResultObject, 0x24);
|
||||||
AppendPointerField(aLine, (std::string(aPrefix) + "_ptr28").c_str(), aResultObject, 0x28);
|
AppendPointerField(aLine, (std::string(aPrefix) + "_ptr28").c_str(), aResultObject, 0x28);
|
||||||
AppendVector3ListCandidate(aLine, (std::string(aPrefix) + "_vec28s12").c_str(), aResultObject, 0x28, 0x30, 12);
|
|
||||||
AppendVectorListCandidate(aLine, (std::string(aPrefix) + "_vec28s16").c_str(), aResultObject, 0x28, 0x30, 16);
|
|
||||||
AppendVectorListCandidate(aLine, (std::string(aPrefix) + "_vec20").c_str(), aResultObject, 0x20, 0x2C, 12);
|
AppendVectorListCandidate(aLine, (std::string(aPrefix) + "_vec20").c_str(), aResultObject, 0x20, 0x2C, 12);
|
||||||
AppendVectorListCandidate(aLine, (std::string(aPrefix) + "_vec20s16").c_str(), aResultObject, 0x20, 0x2C, 16);
|
AppendVectorListCandidate(aLine, (std::string(aPrefix) + "_vec20s16").c_str(), aResultObject, 0x20, 0x2C, 16);
|
||||||
AppendUint32Field(aLine, (std::string(aPrefix) + "_u2c").c_str(), aResultObject, 0x2C);
|
AppendUint32Field(aLine, (std::string(aPrefix) + "_u2c").c_str(), aResultObject, 0x2C);
|
||||||
|
|||||||
Reference in New Issue
Block a user