lore_visibility_test.cpp 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. #include <array>
  2. #include <cassert>
  3. #include <vector>
  4. #include "../Sunrise/src/client/hooks/network/investment/lore_visibility_patch.h"
  5. namespace lore = sunrise::client::hooks::network::investment::lore;
  6. // Model the decoded instructions with arbitrary unlock reads, including a completely fresh save.
  7. bool evaluate(std::span<const lore::Instruction> code, int value) {
  8. std::vector<int> stack;
  9. for (auto instruction : code) {
  10. const auto op = instruction.opcode;
  11. if (op == 1 || op == 10)
  12. stack.push_back(value);
  13. else if (op == 11)
  14. stack.push_back(static_cast<int>(instruction.operand));
  15. else if (op == 2) {
  16. assert(!stack.empty());
  17. stack.back() = !stack.back();
  18. } else {
  19. assert(stack.size() >= 2);
  20. int b = stack.back();
  21. stack.pop_back();
  22. int a = stack.back();
  23. stack.pop_back();
  24. assert(op == 4 || op == 8);
  25. stack.push_back(op == 4 ? (a && b) : (a == b));
  26. }
  27. }
  28. assert(stack.size() == 1);
  29. return stack.back() != 0;
  30. }
  31. int main() {
  32. assert(lore::kTargets.size() == 36);
  33. for (const auto& target : lore::kTargets) {
  34. std::vector<lore::Instruction> code;
  35. switch (target.shape) {
  36. case lore::Shape::constant:
  37. code = {{11, 1}};
  38. break;
  39. case lore::Shape::eva:
  40. code = {{10, 10343}, {2, 0}};
  41. break;
  42. case lore::Shape::confessions:
  43. code = {{1, 8702}, {2, 0}};
  44. break;
  45. case lore::Shape::chronicon:
  46. for (unsigned i = 0; i < 15; ++i) {
  47. code.push_back({10, 10615 + i});
  48. code.push_back({11, 0});
  49. code.push_back({8, UINT32_MAX});
  50. }
  51. for (unsigned i = 0; i < 14; ++i)
  52. code.push_back({4, UINT32_MAX});
  53. break;
  54. }
  55. const auto original = code;
  56. lore::Instruction output{};
  57. assert(lore::replacement(target.shape, code, output));
  58. code[0] = output;
  59. for (int value : {0, 1, 2, 100})
  60. assert(!evaluate(code, value));
  61. for (std::size_t i = 1; i < code.size(); ++i)
  62. assert(code[i] == original[i]);
  63. // Refuse changed schema, wrong operands, and already-modified conditions.
  64. assert(!lore::replacement(target.shape, code, output));
  65. code = original;
  66. code.back().operand ^= 1;
  67. assert(!lore::replacement(target.shape, code, output));
  68. assert(!lore::replacement(target.shape, {}, output));
  69. }
  70. // No chapter completion, reward, or redemption fields occur in the target plan.
  71. for (const auto& target : lore::kTargets) {
  72. assert(target.node ? target.field == 64 : (target.field == 120 || target.field == 136));
  73. }
  74. }