mission_auth_patch_test.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. #include <array>
  2. #include <cassert>
  3. #include <string_view>
  4. #include "../Sunrise/src/middleware/bap/activity_message/mission_auth_patch.h"
  5. #include "../Sunrise/src/middleware/bap/activity_message/squad_auth_body.h"
  6. #include "../Sunrise/src/middleware/bap/activity_message/squad_objective_auth.h"
  7. #include "../Sunrise/src/middleware/bap/activity_message/combatant_path_auth.h"
  8. #include "../Sunrise/src/middleware/bap/activity_message/combatant_delivery_auth.h"
  9. #include "../Sunrise/src/middleware/bap/activity_message/combatant_retire_auth.h"
  10. #include "../Sunrise/src/middleware/bap/activity_message/combatant_action_auth.h"
  11. namespace message = sunrise::middleware::bap::activity_message;
  12. namespace patch = message::mission_auth_patch;
  13. using Bytes = std::array<std::byte,patch::kCapacity>;
  14. // Read a native root's payload, independently of the composition decision.
  15. std::uint64_t value(std::span<const std::byte> body, patch::Field field, unsigned width,
  16. bool optional=true, unsigned skip=0) {
  17. sunrise::middleware::encoding::bits::Reader r(body); std::uint64_t v{};
  18. assert(r.skip(field.offset+(optional?1:0)+skip) && r.read(width,v)); return v;
  19. }
  20. void transport() {
  21. namespace path=message::combatant_path;
  22. namespace cargo=message::combatant_delivery;
  23. namespace retire=message::combatant_retire;
  24. std::array<std::byte,path::kBytes> flight{};
  25. std::array<std::byte,cargo::kBytes> delivery{};
  26. std::array<std::byte,retire::kBytes> disabled{};
  27. assert(path::encode({1,1,0xf6ffb59e,115},flight));
  28. assert(cargo::encode({1,1,0xf6ffb59e,23},delivery));
  29. Bytes retained{}; std::size_t written{},bits{};
  30. assert(patch::compose(path::kSchema,flight,path::kBits,delivery,cargo::kBits,retained,written,bits));
  31. // Independent native packet fixture: spawn + kind3 entry path + one passenger squad.
  32. constexpr std::string_view fixture="800000014c80000001001a3edff6b3cee01cc071f6ffb59e05002e00000004";
  33. assert(bits==246 && written==31);
  34. auto nibble=[](char c) { return c<='9'?c-'0':c-'a'+10; };
  35. for (std::size_t i=0;i<written;++i)
  36. assert(std::to_integer<unsigned>(retained[i])==unsigned((nibble(fixture[2*i])<<4)|nibble(fixture[2*i+1])));
  37. Bytes reversed{}; std::size_t reverseBytes{},reverseBits{};
  38. assert(patch::compose(path::kSchema,delivery,cargo::kBits,flight,path::kBits,reversed,reverseBytes,reverseBits));
  39. assert(reversed==retained && reverseBytes==written && reverseBits==bits);
  40. assert(path::encode({1,2,0xf6ffb59e,116},flight));
  41. assert(patch::compose(path::kSchema,std::span(retained).first(written),bits,
  42. flight,path::kBits,retained,written,bits)); // in-place estate update
  43. patch::Layout layout{};
  44. assert(patch::parse(path::kSchema,std::span(retained).first(written),bits,layout));
  45. assert(value(retained,layout.fields[0],31)==1);
  46. assert(value(retained,layout.fields[6],31)==2);
  47. assert(value(retained,layout.fields[7],31,true,4+55)==1); // exit preserves delivery revision
  48. namespace action = message::combatant_action;
  49. std::array<std::byte,action::kBytes> animation{};
  50. assert(action::encode({1,3,0x811c9dc5,0x7b0d3643},animation));
  51. assert(action::validate(animation,action::kBits));
  52. assert(!action::validate(animation,action::kBits-1));
  53. auto badPadding = animation; badPadding.back() |= std::byte{1};
  54. assert(!action::validate(badPadding,action::kBits));
  55. assert(!action::encode({0,3,0x811c9dc5,0x7b0d3643},badPadding));
  56. assert(!action::encode({1,0,0x811c9dc5,0x7b0d3643},badPadding));
  57. assert(!action::encode({1,3,0x811c9dc5,0x811c9dc5},badPadding));
  58. assert(patch::compose(path::kSchema,std::span(retained).first(written),bits,
  59. animation,action::kBits,retained,written,bits));
  60. assert(patch::parse(path::kSchema,std::span(retained).first(written),bits,layout));
  61. assert(value(retained,layout.fields[0],31)==1); // no actor recreation
  62. assert(value(retained,layout.fields[6],31)==3);
  63. assert(value(retained,layout.fields[6],4,true,31+6+6+1)==10); // kind 9
  64. assert(value(retained,layout.fields[6],32,true,31+6+6+1+4+2)==0x811c9dc5);
  65. assert(value(retained,layout.fields[6],32,true,31+6+6+1+4+2+32)==0x7b0d3643);
  66. assert(value(retained,layout.fields[7],31,true,4+55)==1); // delivery remains completed
  67. assert(retire::encode(2,disabled));
  68. assert(patch::compose(path::kSchema,std::span(retained).first(written),bits,
  69. disabled,retire::kBits,retained,written,bits));
  70. assert(patch::parse(path::kSchema,std::span(retained).first(written),bits,layout));
  71. assert(value(retained,layout.fields[0],31)==2); // AB71E0 lifecycle reset
  72. assert(value(retained,layout.fields[3],1,false)==0);
  73. assert(value(retained,layout.fields[7],4)==0); // retired ships cannot re-request passengers
  74. assert(value(retained,layout.fields[7],31,true,4)==2);
  75. assert(value(retained,layout.fields[6],31)==3);
  76. const auto good=retained;
  77. assert(!patch::compose(path::kSchema,{},0,std::span(delivery).first(16),cargo::kBits,retained,written,bits));
  78. assert(retained==good);
  79. assert(!patch::compose(path::kSchema,{},0,flight,path::kBits,std::span(retained).first(1),written,bits));
  80. assert(retained==good);
  81. }
  82. void squad() {
  83. namespace spawn=message::squad_auth;
  84. namespace objective=message::squad_objective;
  85. std::array<std::int32_t,2> counts{3,1};
  86. Bytes placement{},retained{}; std::size_t placementBytes{},placementBits{},written{},bits{};
  87. assert(spawn::encode({counts,1,spawn::Mode::reserve},{},placement,placementBytes,placementBits));
  88. std::array<std::byte,objective::kBytes> ai{};
  89. assert(objective::encode({0xf6ffb59e,1,15,-1,true},ai));
  90. assert(patch::compose(spawn::kSchema,std::span(placement).first(placementBytes),placementBits,
  91. ai,objective::kBits,retained,written,bits));
  92. for (int group : {0,5,12,-1}) {
  93. assert(objective::encode({0xf6ffb59e,1,15,group,true},ai));
  94. assert(patch::compose(spawn::kSchema,std::span(retained).first(written),bits,
  95. ai,objective::kBits,retained,written,bits));
  96. patch::Layout layout{};
  97. assert(patch::parse(spawn::kSchema,std::span(retained).first(written),bits,layout));
  98. assert(value(retained,layout.fields[0],32)==0xf6ffb59e);
  99. assert(value(retained,layout.fields[3],4)==2);
  100. assert(value(retained,layout.fields[3],32,true,4)==0x80000003);
  101. assert(value(retained,layout.fields[3],32,true,36)==0x80000001);
  102. assert(value(retained,layout.fields[6],31)==1); // AI never creates a new spawn generation
  103. assert(value(retained,layout.fields[18],2,false)==2); // populated: native mode 1
  104. assert(value(retained,layout.fields[16],5)==unsigned(group+1));
  105. assert(value(retained,layout.fields[19],3,false)==4); // reserved mode survives
  106. }
  107. counts={0,0};
  108. assert(spawn::encode({counts,2,spawn::Mode::reserve},{1,true},placement,placementBytes,placementBits));
  109. assert(patch::compose(spawn::kSchema,std::span(retained).first(written),bits,
  110. std::span(placement).first(placementBytes),placementBits,retained,written,bits));
  111. patch::Layout layout{};
  112. assert(patch::parse(spawn::kSchema,std::span(retained).first(written),bits,layout));
  113. assert(value(retained,layout.fields[3],32,true,4)==0x80000000);
  114. assert(value(retained,layout.fields[3],32,true,36)==0x80000000);
  115. assert(value(retained,layout.fields[6],31)==2);
  116. assert(value(retained,layout.fields[18],2,false)==1); // zero counts: native destruction, not death
  117. assert(value(retained,layout.fields[0],32)==0xf6ffb59e);
  118. // A checkpoint's later spawn restores the populated mode and advances generation;
  119. // a composition with empty categories must not be mistaken for retirement.
  120. counts={0,1};
  121. assert(spawn::encode({counts,3,spawn::Mode::mode2},{2,true},placement,placementBytes,placementBits));
  122. assert(patch::compose(spawn::kSchema,std::span(retained).first(written),bits,
  123. std::span(placement).first(placementBytes),placementBits,retained,written,bits));
  124. assert(patch::parse(spawn::kSchema,std::span(retained).first(written),bits,layout));
  125. assert(value(retained,layout.fields[6],31)==3);
  126. assert(value(retained,layout.fields[18],2,false)==2);
  127. assert(value(retained,layout.fields[3],32,true,36)==0x80000001);
  128. // A full eight-category squad plus objective exceeds the old spawn-only lease.
  129. const std::array<std::int32_t,8> largeCounts{1,1,1,1,1,1,1,1};
  130. assert(spawn::encode({largeCounts,1,spawn::Mode::mode2},{},placement,placementBytes,placementBits));
  131. assert(patch::compose(spawn::kSchema,std::span(placement).first(placementBytes),placementBits,
  132. ai,objective::kBits,retained,written,bits));
  133. assert(written > spawn::kMaximumByteCount && written <= spawn::kMaximumRetainedByteCount);
  134. }
  135. void manifest_transport() {
  136. namespace cargo = sunrise::middleware::bap::activity_message::combatant_delivery;
  137. namespace path = sunrise::middleware::bap::activity_message::combatant_path;
  138. namespace patch = sunrise::middleware::bap::activity_message::mission_auth_patch;
  139. const std::array<cargo::SquadReference,2> manifest{{{0xf6ffb59e,23},{0xf6ffb59e,25}}};
  140. std::array<std::byte,cargo::kMaxBytes> cargoBody{};
  141. std::array<std::byte,path::kBytes> pathBody{};
  142. Bytes retained{}; std::size_t n{},bits{},written{},retainedBits{};
  143. assert(cargo::encode_many(1,1,manifest,cargoBody,n,bits));
  144. assert(path::encode({1,1,0xf6ffb59e,115},pathBody));
  145. assert(patch::compose(path::kSchema,pathBody,path::kBits,std::span(cargoBody).first(n),bits,
  146. retained,written,retainedBits));
  147. assert(path::encode({1,2,0xf6ffb59e,116},pathBody));
  148. assert(patch::compose(path::kSchema,std::span(retained).first(written),retainedBits,pathBody,path::kBits,
  149. retained,written,retainedBits));
  150. patch::Layout layout{};
  151. assert(patch::parse(path::kSchema,std::span(retained).first(written),retainedBits,layout));
  152. assert(value(retained,layout.fields[6],31)==2);
  153. assert(value(retained,layout.fields[7],4)==2);
  154. assert(value(retained,layout.fields[7],32,true,4)==0xf6ffb59e);
  155. assert(value(retained,layout.fields[7],16,true,4+32+7)==32768+23);
  156. assert(value(retained,layout.fields[7],16,true,4+55+32+7)==32768+25);
  157. }
  158. int main() { transport(); squad(); manifest_transport(); }