Files
iChris4 db30945f4c Added First Person VR item management
- Implemented DVDReadVrAsset function to read mapped disc paths for VR assets.
- Created HeldItem structure and ReadHeldItem function for managing held items in the game.
- Developed unit tests for ReadHeldItem to ensure correct functionality and edge case handling.
- Added cockpit item data tests to validate model indexing and parsing of archives.
2026-09-29 23:55:52 +02:00

85 lines
3.6 KiB
C++

#include "vr/mkw_vr_item.h"
#include <cstdint>
#include <iostream>
#include <stdexcept>
#include <unordered_map>
namespace {
struct GuestMemory {
using AccessViolation = std::out_of_range;
inline static std::unordered_map<uint32_t, uint8_t> bytes;
inline static uint32_t fault = 0;
static bool Contains(uint32_t address, uint32_t length) {
for (uint32_t i = 0; i < length; ++i) if (!bytes.count(address + i)) return false;
return true;
}
static uint8_t Read8(uint32_t address) {
if (address == fault) throw AccessViolation("fault");
return bytes.at(address);
}
static uint32_t Read32(uint32_t address) {
uint32_t value = 0;
for (uint32_t i = 0; i < 4; ++i) value = (value << 8) | Read8(address + i);
return value;
}
static bool TryRead32(uint32_t address, uint32_t& out) {
try { out = Read32(address); return true; } catch (const AccessViolation&) { return false; }
}
static void Write32(uint32_t address, uint32_t value) {
for (uint32_t i = 0; i < 4; ++i) bytes[address + i] = uint8_t(value >> (24 - i * 8));
}
};
constexpr uint32_t manager = 0x81000000u, players = 0x82000000u;
constexpr uint32_t player = players + 7 * 0x248u;
int failures = 0;
void Check(bool value, const char* name) {
if (!value) { ++failures; std::cerr << "FAILED: " << name << '\n'; }
}
void Init() {
GuestMemory::bytes.clear(); GuestMemory::fault = 0;
GuestMemory::Write32(0x809C3618u, manager);
GuestMemory::Write32(manager + 0x14u, players);
for (uint32_t i = 0; i < 0x94u; ++i) GuestMemory::bytes[player + i] = 0;
GuestMemory::bytes[player + 0x18u] = 7;
GuestMemory::Write32(player + 0x90u, 1);
}
}
int main() {
using mkw::vr::detail::ReadHeldItem;
for (uint32_t id = 0; id <= 0x12u; ++id) {
Init(); GuestMemory::Write32(player + 0x8cu, id);
const auto item = ReadHeldItem<GuestMemory>(7, 19);
Check(item.valid && item.id == id && item.count == 1 && item.race_generation == 19,
"all 19 IDs, nonzero racer, generation");
}
Init(); GuestMemory::Write32(player + 0x8cu, 0x10); GuestMemory::Write32(player + 0x90u, 3);
Check(ReadHeldItem<GuestMemory>(7, 1).count == 3, "triple full");
GuestMemory::Write32(player + 0x90u, 2);
Check(ReadHeldItem<GuestMemory>(7, 1).count == 2, "triple remaining");
GuestMemory::Write32(player + 0x90u, 0);
Check(!ReadHeldItem<GuestMemory>(7, 1).valid, "inventory removed on use or damage");
GuestMemory::Write32(player + 0x90u, 1);
GuestMemory::Write32(player + 0x58u, 1);
Check(!ReadHeldItem<GuestMemory>(7, 1).valid, "roulette spinning");
GuestMemory::Write32(player + 0x58u, 0);
for (uint32_t id : {0x13u, 0x14u, 0xffu}) {
GuestMemory::Write32(player + 0x8cu, id);
Check(!ReadHeldItem<GuestMemory>(7, 1).valid, "empty or unsupported");
}
Init(); GuestMemory::Write32(player + 0x8cu, 0x0au);
Check(ReadHeldItem<GuestMemory>(7, 1).valid, "golden mushroom held");
GuestMemory::Write32(player + 0x90u, 0);
Check(!ReadHeldItem<GuestMemory>(7, 1).valid, "golden mushroom expiry");
Init(); GuestMemory::fault = player + 0x8cu;
const auto faulted = ReadHeldItem<GuestMemory>(7, 2);
Check(!faulted.valid && faulted.race_generation == 2, "read fault preserves generation");
Init(); GuestMemory::Write32(0x809C3618u, 0);
Check(!ReadHeldItem<GuestMemory>(7, 1).valid, "invalid manager");
Init(); GuestMemory::bytes[player + 0x18u] = 0;
Check(!ReadHeldItem<GuestMemory>(7, 1).valid, "racer identity mismatch");
return failures ? 1 : 0;
}