diff --git a/src/main.cpp b/src/main.cpp index 4228b0d..56efbb4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,35 +2,15 @@ #include #include #include -#include - -#include "xoshiro.hpp" +#include namespace asio = boost::asio; -// nvm doesnt work lol :) -// using asio::experimental::concurrent_channel; -template -std::string RandomString(std::uint32_t length, TRandomAlgo& rng) { - std::string s; - s.resize(length); - while(true) { - auto res = rng(); - auto acc = 0u; - - // I love bit hacks!!! - for(std::size_t i = 0; i < sizeof(res); ++i) { - std::uint8_t c = (res >> acc); - if(islower(c)) { - if(length-- <= 0) - return s; - s.push_back(c); - } - acc += CHAR_BIT; - } - } -} +constexpr static auto MAX_CODE_LENGTH = 8; +constexpr static auto THREAD_COUNT = 26; // this is a-z +/// This is a constexpr-safe zero-allocation version of the algorithm +/// the "Scramble" ConsoleScript hash algorithm. constexpr std::uint32_t SwsfScramble(std::string_view code) { std::uint32_t tally {}; @@ -47,13 +27,26 @@ constexpr std::uint32_t SwsfScramble(std::string_view code) { return tally; } -// These is not atomic since no other threads update it -// there is no real point to making it atomic, other than -// -// maybe preventing some false sharing or other perf problem -// ... but I don't think that matters, lol -std::uint32_t code_hash = 0x2c73af55; -std::uint32_t code_length = 8; +struct FputcIterator { + using iterator_category = std::output_iterator_tag; + using value_type = void; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = void; + + FputcIterator(std::FILE* file) : file(file) {} + FputcIterator& operator*() { return *this; } + FputcIterator& operator++() { return *this; } + FputcIterator& operator++(int) { return *this; } + + FputcIterator& operator=(const char& val) { + fputc(val, file); + return *this; + } + + private: + std::FILE* file; +}; bool NextCode(std::string& buffer, size_t start) { size_t len = buffer.length(); @@ -87,10 +80,15 @@ namespace { return "\r" + (m < 0 ? "\33[" + std::to_string(-m) + 'A' : std::string(m, '\n')); } + std::string_view ResetLine() { + return "\33[2J"; + } + } // namespace -struct ThreadDisplayData { - ThreadDisplayData() { codeString.resize(8); } +/// This class implements the display of progress +struct ThreadInfoData { + ThreadInfoData() { codeString.resize(MAX_CODE_LENGTH); } std::uint32_t threadIndex; std::string codeString; @@ -98,104 +96,118 @@ struct ThreadDisplayData { bool done = false; void DisplayProgress() { - struct FputcIterator { - using iterator_category = std::output_iterator_tag; - using value_type = void; - using difference_type = std::ptrdiff_t; - using pointer = void; - using reference = void; - - FputcIterator(std::FILE* file) : file(file) {} - FputcIterator& operator*() { return *this; } - FputcIterator& operator++() { return *this; } - FputcIterator& operator++(int) { return *this; } - - FputcIterator& operator=(const char& val) { - fputc(val, file); - return *this; - } - - private: - std::FILE* file; - }; - if(!done) std::format_to(FputcIterator(stdout), "{}Thread {:2}: Trying code {}{} {}({:08x}){}", Line(threadIndex), threadIndex, Color(172), codeString, Color(166), codeHash, Reset()); else { - std::format_to(FputcIterator(stdout), "{}Thread {:2}: {}Finished!{}", Line(threadIndex), threadIndex, Color(172), Reset()); + std::format_to(FputcIterator(stdout), "{}{}Thread {:2}: {}Finished!{}", Line(threadIndex), ResetLine(), threadIndex, Color(172), Reset()); } std::fflush(stdout); } }; -std::vector data(26); +std::vector infoData(THREAD_COUNT); -struct BruteThreadState { - BruteThreadState(unsigned tid) : threadIndex(tid) {} +/// This class actually implements the brunt of the logic +struct BruteforceWorker { - void BruteForce(char prefix) { - // for(std::uint32_t i = 8; i <= code_length; ++i) { - test_buffer.resize(code_length, 'a'); + /// Options for the worker. + struct Options { + /// The target hash to find matches for. + std::uint32_t targetHash; + + /// If `startLength` should be treated as the code length. + bool exact; + + std::uint32_t startLength; + std::uint32_t endLength; + }; + + constexpr BruteforceWorker(unsigned tid, const Options& options) : options(options), threadIndex(tid) {} + + /// Public-facing driver function - the thread pool runs this. + void Bruteforce(char prefix) { + if(options.exact) { + BruteforceForLength(prefix, options.startLength); + } else { + for(std::uint32_t i = options.startLength; i <= options.endLength; ++i) + BruteforceForLength(prefix, i); + } + + DisplayData().done = true; + } + + private: + + void BruteforceForLength(char prefix, std::uint32_t length) { + DisplayData().codeString.resize(length); + test_buffer.resize(length, 'a'); test_buffer[0] = prefix; // test all possible combinations while(true) { - // std::string test_buffer = RandomString(code_length, rng); hash = SwsfScramble(test_buffer); - if(hash == code_hash) { - std::cerr << std::format("match: {} ({:08x})\n", test_buffer, hash); - } - memcpy(data[threadIndex].codeString.data(), test_buffer.data(), test_buffer.length()); - data[threadIndex].codeHash = hash; + // There was a match! + if(hash == options.targetHash) + std::format_to(FputcIterator(stderr), "match: {} ({:08x})\n", test_buffer, hash); - // DisplayProgress(test_buffer, hash); + CopyDisplayData(); if(!NextCode(test_buffer, 1)) break; } - //} - - data[threadIndex].done = true; } - std::string test_buffer; - std::uint32_t hash; + void CopyDisplayData() { + memcpy(DisplayData().codeString.data(), test_buffer.data(), test_buffer.length()); + DisplayData().codeHash = hash; + } + + constexpr ThreadInfoData& DisplayData() { return infoData[threadIndex]; } + + Options options; unsigned threadIndex; - std::random_device rd; - // swbf::Xoshiro256ss rng{rd}; + std::string test_buffer; + std::uint32_t hash; }; int main() { asio::io_context ioc; - asio::thread_pool pool(27); + asio::thread_pool pool(THREAD_COUNT); auto threadIndex = 0u; + BruteforceWorker::Options options { + .targetHash = 0x2c73af55, + .exact = true, + .startLength = 8 + }; + // post worker threads to run onto the thread pool & wait for them to complete - for(int i = 0; i < 26; ++i) - asio::post(pool, [&, i, tid = threadIndex++]() { - BruteThreadState state(tid); - data[tid].threadIndex = tid; - state.BruteForce('a' + i); + for(int i = 0; i < THREAD_COUNT; ++i) + asio::post(pool, [&, tid = threadIndex++]() { + BruteforceWorker state(tid, options); + infoData[tid].threadIndex = tid; + state.Bruteforce('a' + tid); }); bool done = false; while(!done) { int doneIndex = 0; - for(auto& d : data) { + for(auto& d : infoData) { if(d.done) - doneIndex++; + doneIndex++; d.DisplayProgress(); } - if(doneIndex == 26) { + if(doneIndex == THREAD_COUNT) done = true; - } + + std::this_thread::sleep_for(std::chrono::milliseconds(66)); } pool.join(); // just in case! diff --git a/src/xoshiro.hpp b/src/xoshiro.hpp deleted file mode 100644 index f4351d8..0000000 --- a/src/xoshiro.hpp +++ /dev/null @@ -1,85 +0,0 @@ -#include -#include -#include // CHAR_BIT -#include - -namespace swbf { - namespace detail { - template - constexpr size_t BitSizeOf() { - return sizeof(T) * CHAR_BIT; - } - - constexpr std::uint64_t splitmix64(std::uint64_t x) { - std::uint64_t z = (x += 0x9e3779b97f4a7c15uLL); - z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9uLL; - z = (z ^ (z >> 27)) * 0x94d049bb133111ebuLL; - return z ^ (z >> 31); - } - - constexpr std::uint64_t rotl(std::uint64_t x, int k) { - return (x << k) | (x >> (BitSizeOf() - k)); - } - - /** - * Xoshiro256** as a C++ RandomNumberEngine, - * which can be used in C++ random algorithms. - */ - struct Xoshiro256ss { - using result_type = std::uint64_t; - - std::uint64_t s[4] {}; - - constexpr explicit Xoshiro256ss() : Xoshiro256ss(0) {} - - constexpr explicit Xoshiro256ss(std::uint64_t seed) { - s[0] = splitmix64(seed); - s[1] = splitmix64(seed); - s[2] = splitmix64(seed); - s[3] = splitmix64(seed); - } - - /** - * Constructor for seeding from a `std::random_device`. - * - * \param[in] rd Random device to seed this engine with. - */ - constexpr explicit Xoshiro256ss(std::random_device& rd) { - // Get 64 bits out of the random device. - // - // This lambda is quite literal, as it fetches - // 2 iterations of the random engine, and - // shifts + OR's them into a 64bit value. - auto get_u64 = [&rd] { - std::uint64_t the_thing = rd(); - return (the_thing << 32) | rd(); - }; - - // seed with 256 bits of entropy from the random device + splitmix64 - // to ensure we seed it well, as per recommendation. - s[0] = splitmix64(get_u64()); - s[1] = splitmix64(get_u64()); - s[2] = splitmix64(get_u64()); - s[3] = splitmix64(get_u64()); - } - - static constexpr result_type min() { return 0; } - - static constexpr result_type max() { return std::uint64_t(-1); } - - constexpr result_type operator()() { - result_type result = rotl(s[1] * 5, 7) * 9; - result_type t = s[1] << 17; - s[2] ^= s[0]; - s[3] ^= s[1]; - s[1] ^= s[2]; - s[0] ^= s[3]; - s[2] ^= t; - s[3] = rotl(s[3], 45); - return result; - } - }; - } // namespace detail - - using detail::Xoshiro256ss; -} // namespace swbf