LaggedFibonacciGenerator.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. // SPDX-License-Identifier: CC0-1.0
  2. #include "DiscIO/LaggedFibonacciGenerator.h"
  3. #include <algorithm>
  4. #include <cstddef>
  5. #include <cstring>
  6. #include "Common/Align.h"
  7. #include "Common/Assert.h"
  8. #include "Common/CommonTypes.h"
  9. #include "Common/Swap.h"
  10. namespace DiscIO
  11. {
  12. void LaggedFibonacciGenerator::SetSeed(const u32 seed[SEED_SIZE])
  13. {
  14. SetSeed(reinterpret_cast<const u8*>(seed));
  15. }
  16. void LaggedFibonacciGenerator::SetSeed(const u8 seed[SEED_SIZE * sizeof(u32)])
  17. {
  18. m_position_bytes = 0;
  19. for (size_t i = 0; i < SEED_SIZE; ++i)
  20. m_buffer[i] = Common::swap32(seed + i * sizeof(u32));
  21. Initialize(false);
  22. }
  23. size_t LaggedFibonacciGenerator::GetSeed(const u8* data, size_t size, size_t data_offset,
  24. u32 seed_out[SEED_SIZE])
  25. {
  26. if ((reinterpret_cast<uintptr_t>(data) - data_offset) % alignof(u32) != 0)
  27. {
  28. ASSERT(false);
  29. return 0;
  30. }
  31. // For code simplicity, only include whole u32 words when regenerating the seed. It would be
  32. // possible to get rid of this restriction and use a few additional bytes, but it's probably more
  33. // effort than it's worth considering that junk data often starts or ends on 4-byte offsets.
  34. const size_t bytes_to_skip = Common::AlignUp(data_offset, sizeof(u32)) - data_offset;
  35. const u32* u32_data = reinterpret_cast<const u32*>(data + bytes_to_skip);
  36. const size_t u32_size = (size - bytes_to_skip) / sizeof(u32);
  37. const size_t u32_data_offset = (data_offset + bytes_to_skip) / sizeof(u32);
  38. LaggedFibonacciGenerator lfg;
  39. if (!GetSeed(u32_data, u32_size, u32_data_offset, &lfg, seed_out))
  40. return false;
  41. lfg.m_position_bytes = data_offset % (LFG_K * sizeof(u32));
  42. const u8* end = data + size;
  43. size_t reconstructed_bytes = 0;
  44. while (data < end && lfg.GetByte() == *data)
  45. {
  46. ++reconstructed_bytes;
  47. ++data;
  48. }
  49. return reconstructed_bytes;
  50. }
  51. bool LaggedFibonacciGenerator::GetSeed(const u32* data, size_t size, size_t data_offset,
  52. LaggedFibonacciGenerator* lfg, u32 seed_out[SEED_SIZE])
  53. {
  54. if (size < LFG_K)
  55. return false;
  56. // If the data doesn't look like something we can regenerate, return early to save time
  57. if (!std::all_of(data, data + LFG_K, [](u32 x) {
  58. return (Common::swap32(x) & 0x00C00000) == (Common::swap32(x) >> 2 & 0x00C00000);
  59. }))
  60. {
  61. return false;
  62. }
  63. const size_t data_offset_mod_k = data_offset % LFG_K;
  64. const size_t data_offset_div_k = data_offset / LFG_K;
  65. std::copy_n(data, LFG_K - data_offset_mod_k, lfg->m_buffer.data() + data_offset_mod_k);
  66. std::copy_n(data + LFG_K - data_offset_mod_k, data_offset_mod_k, lfg->m_buffer.data());
  67. lfg->Backward(0, data_offset_mod_k);
  68. for (size_t i = 0; i < data_offset_div_k; ++i)
  69. lfg->Backward();
  70. if (!lfg->Reinitialize(seed_out))
  71. return false;
  72. for (size_t i = 0; i < data_offset_div_k; ++i)
  73. lfg->Forward();
  74. return true;
  75. }
  76. void LaggedFibonacciGenerator::GetBytes(size_t count, u8* out)
  77. {
  78. while (count > 0)
  79. {
  80. const size_t length = std::min(count, LFG_K * sizeof(u32) - m_position_bytes);
  81. std::memcpy(out, reinterpret_cast<u8*>(m_buffer.data()) + m_position_bytes, length);
  82. m_position_bytes += length;
  83. count -= length;
  84. out += length;
  85. if (m_position_bytes == LFG_K * sizeof(u32))
  86. {
  87. Forward();
  88. m_position_bytes = 0;
  89. }
  90. }
  91. }
  92. u8 LaggedFibonacciGenerator::GetByte()
  93. {
  94. const u8 result = reinterpret_cast<u8*>(m_buffer.data())[m_position_bytes];
  95. ++m_position_bytes;
  96. if (m_position_bytes == LFG_K * sizeof(u32))
  97. {
  98. Forward();
  99. m_position_bytes = 0;
  100. }
  101. return result;
  102. }
  103. void LaggedFibonacciGenerator::Forward(size_t count)
  104. {
  105. m_position_bytes += count;
  106. while (m_position_bytes >= LFG_K * sizeof(u32))
  107. {
  108. Forward();
  109. m_position_bytes -= LFG_K * sizeof(u32);
  110. }
  111. }
  112. void LaggedFibonacciGenerator::Forward()
  113. {
  114. for (size_t i = 0; i < LFG_J; ++i)
  115. m_buffer[i] ^= m_buffer[i + LFG_K - LFG_J];
  116. for (size_t i = LFG_J; i < LFG_K; ++i)
  117. m_buffer[i] ^= m_buffer[i - LFG_J];
  118. }
  119. void LaggedFibonacciGenerator::Backward(size_t start_word, size_t end_word)
  120. {
  121. const size_t loop_end = std::max(LFG_J, start_word);
  122. for (size_t i = std::min(end_word, LFG_K); i > loop_end; --i)
  123. m_buffer[i - 1] ^= m_buffer[i - 1 - LFG_J];
  124. for (size_t i = std::min(end_word, LFG_J); i > start_word; --i)
  125. m_buffer[i - 1] ^= m_buffer[i - 1 + LFG_K - LFG_J];
  126. }
  127. bool LaggedFibonacciGenerator::Reinitialize(u32 seed_out[SEED_SIZE])
  128. {
  129. for (size_t i = 0; i < 4; ++i)
  130. Backward();
  131. for (u32& x : m_buffer)
  132. x = Common::swap32(x);
  133. // Reconstruct the bits which are missing due to the output code shifting by 18 instead of 16.
  134. // Unfortunately we can't reconstruct bits 16 and 17 (counting LSB as 0) for the first word,
  135. // but the observable result (when shifting by 18 instead of 16) is not affected by this.
  136. for (size_t i = 0; i < SEED_SIZE; ++i)
  137. {
  138. m_buffer[i] = (m_buffer[i] & 0xFF00FFFF) | (m_buffer[i] << 2 & 0x00FC0000) |
  139. ((m_buffer[i + 16] ^ m_buffer[i + 15]) << 9 & 0x00030000);
  140. }
  141. for (size_t i = 0; i < SEED_SIZE; ++i)
  142. seed_out[i] = Common::swap32(m_buffer[i]);
  143. return Initialize(true);
  144. }
  145. bool LaggedFibonacciGenerator::Initialize(bool check_existing_data)
  146. {
  147. for (size_t i = SEED_SIZE; i < LFG_K; ++i)
  148. {
  149. const u32 calculated = (m_buffer[i - 17] << 23) ^ (m_buffer[i - 16] >> 9) ^ m_buffer[i - 1];
  150. if (check_existing_data)
  151. {
  152. const u32 actual = (m_buffer[i] & 0xFF00FFFF) | (m_buffer[i] << 2 & 0x00FC0000);
  153. if ((calculated & 0xFFFCFFFF) != actual)
  154. return false;
  155. }
  156. m_buffer[i] = calculated;
  157. }
  158. // Instead of doing the "shift by 18 instead of 16" oddity when actually outputting the data,
  159. // we can do the shifting (and byteswapping) at this point to make the output code simpler.
  160. for (u32& x : m_buffer)
  161. x = Common::swap32((x & 0xFF00FFFF) | ((x >> 2) & 0x00FF0000));
  162. for (size_t i = 0; i < 4; ++i)
  163. Forward();
  164. return true;
  165. }
  166. } // namespace DiscIO