#include "utils.hpp" #if defined (__linux__) && defined(XLIB_BASED) #include #endif #ifdef _WIN32 #include #endif #include #include #include #include #include #include #include #include #include #ifdef SFML_SYSTEM_ANDROID // Since we want to get the native activity from SFML, we'll have to use an // extra header here: #include #endif #ifdef ANDROID_COMPILATION #include // These headers are only needed for direct NDK/JDK interaction #include #endif #define xstr(a) str(a) #define str(a) #a #define APP_VERSION_MAJOR 1 #define APP_VERSION_MINOR 2 #define APP_VERSION_REVISION 0 #ifdef RASPI #define APP_VERSION_LETTER R #else #define APP_VERSION_LETTER M #endif static bool checked_be_once = false; static bool _is_be = false; std::chrono::time_point clock_start_program; bool is_big_endian(void) { if(checked_be_once) return _is_be; union { uint32_t i; char c[4]; } value = {0x01020304}; checked_be_once = true; _is_be = value.c[0] == 1; return _is_be; } uint32_t reverse_endianness(uint32_t value) { return ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) | ((value & 0xFF000000) >> 24); } uint16_t reverse_endianness(uint16_t value) { return ((value & 0xFF) << 8) | ((value & 0xFF00) >> 8); } uint32_t to_le(uint32_t value) { if(is_big_endian()) value = reverse_endianness(value); return value; } uint32_t to_be(uint32_t value) { if(!is_big_endian()) value = reverse_endianness(value); return value; } uint16_t to_le(uint16_t value) { if(is_big_endian()) value = reverse_endianness(value); return value; } uint16_t to_be(uint16_t value) { if(!is_big_endian()) value = reverse_endianness(value); return value; } uint32_t from_le(uint32_t value) { if(is_big_endian()) value = reverse_endianness(value); return value; } uint32_t from_be(uint32_t value) { if(!is_big_endian()) value = reverse_endianness(value); return value; } uint16_t from_le(uint16_t value) { if(is_big_endian()) value = reverse_endianness(value); return value; } uint16_t from_be(uint16_t value) { if(!is_big_endian()) value = reverse_endianness(value); return value; } uint16_t read_le16(const uint8_t* data, size_t count, size_t multiplier) { data += count * multiplier; return data[0] | (data[1] << 8); } uint16_t read_be16(const uint8_t* data, size_t count, size_t multiplier) { data += count * multiplier; return data[1] | (data[0] << 8); } uint32_t read_le32(const uint8_t* data, size_t count, size_t multiplier) { data += count * multiplier; return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); } uint32_t read_be32(const uint8_t* data, size_t count, size_t multiplier) { data += count * multiplier; return data[3] | (data[2] << 8) | (data[1] << 16) | (data[0] << 24); } uint64_t read_le64(const uint8_t* data, size_t count, size_t multiplier) { data += count * multiplier; return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24) | (((uint64_t)data[4]) << 32) | (((uint64_t)data[5]) << 40) | (((uint64_t)data[6]) << 48) | (((uint64_t)data[7]) << 56); } uint64_t read_be64(const uint8_t* data, size_t count, size_t multiplier) { data += count * multiplier; return data[7] | (data[6] << 8) | (data[5] << 16) | (data[4] << 24) | (((uint64_t)data[3]) << 32) | (((uint64_t)data[2]) << 40) | (((uint64_t)data[1]) << 48) | (((uint64_t)data[0]) << 56); } void write_le16(uint8_t* data, uint16_t value, size_t count, size_t multiplier) { data += count * multiplier; data[0] = value & 0xFF; data[1] = (value >> 8) & 0xFF; } void write_be16(uint8_t* data, uint16_t value, size_t count, size_t multiplier) { data += count * multiplier; data[0] = (value >> 8) & 0xFF; data[1] = value & 0xFF; } void write_le32(uint8_t* data, uint32_t value, size_t count, size_t multiplier) { data += count * multiplier; data[0] = value & 0xFF; data[1] = (value >> 8) & 0xFF; data[2] = (value >> 16) & 0xFF; data[3] = (value >> 24) & 0xFF; } void write_be32(uint8_t* data, uint32_t value, size_t count, size_t multiplier) { data += count * multiplier; data[0] = (value >> 24) & 0xFF; data[1] = (value >> 16) & 0xFF; data[2] = (value >> 8) & 0xFF; data[3] = value & 0xFF; } void write_le64(uint8_t* data, uint64_t value, size_t count, size_t multiplier) { data += count * multiplier; data[0] = value & 0xFF; data[1] = (value >> 8) & 0xFF; data[2] = (value >> 16) & 0xFF; data[3] = (value >> 24) & 0xFF; data[4] = (value >> 32) & 0xFF; data[5] = (value >> 40) & 0xFF; data[6] = (value >> 48) & 0xFF; data[7] = (value >> 56) & 0xFF; } void write_be64(uint8_t* data, uint64_t value, size_t count, size_t multiplier) { data += count * multiplier; data[0] = (value >> 56) & 0xFF; data[1] = (value >> 48) & 0xFF; data[2] = (value >> 40) & 0xFF; data[3] = (value >> 32) & 0xFF; data[4] = (value >> 24) & 0xFF; data[5] = (value >> 16) & 0xFF; data[6] = (value >> 8) & 0xFF; data[7] = value & 0xFF; } void write_string(uint8_t* data, std::string text) { for(size_t i = 0; i < text.size(); i++) data[i] = (uint8_t)text[i]; } std::string read_string(uint8_t* data, size_t size) { uint8_t* new_data = new uint8_t[size + 1]; memcpy(new_data, data, size); new_data[size] = '\0'; std::string out = std::string((const char*)new_data); delete []new_data; return out; } uint32_t rotate_bits_left(uint32_t value) { return (value << 1) | ((value & 0x80000000) >> 31); } uint32_t rotate_bits_right(uint32_t value) { return (value >> 1) | ((value & 1) << 31); } void init_start_time() { clock_start_program = std::chrono::high_resolution_clock::now(); } uint32_t ms_since_start() { const auto curr_time = std::chrono::high_resolution_clock::now(); std::chrono::durationdiff = curr_time - clock_start_program; return (uint32_t)(diff.count() * 1000); } std::string to_hex(uint16_t value) { const int num_digits = sizeof(value) * 2; char digits[num_digits]; for(int i = 0; i < num_digits; i++) { uint8_t subvalue = (value >> (4 * (num_digits - 1 - i))) & 0xF; char digit = '0' + subvalue; if(subvalue >= 0xA) digit = 'A' + (subvalue - 0xA); digits[i] = digit; } return static_cast(digits); } void init_threads(void) { #if defined(__linux__) && defined(XLIB_BASED) XInitThreads(); #endif /* #ifdef _WIN32 timeBeginPeriod(4); #endif */ } void complete_threads(void) { /* #ifdef _WIN32 timeEndPeriod(4); #endif */ } std::string get_version_string(bool get_letter) { std::string version_str = std::to_string(APP_VERSION_MAJOR) + "." + std::to_string(APP_VERSION_MINOR) + "." + std::to_string(APP_VERSION_REVISION); if(get_letter) return version_str + xstr(APP_VERSION_LETTER); return version_str; } std::string get_float_str_decimals(float value, int decimals) { float approx_factor = (float)(pow(0.1f, decimals) * (0.5f)); int int_part = (int)(value + approx_factor); int dec_part = (int)((value + approx_factor - int_part) * pow(10, decimals)); std::string return_text = std::to_string(int_part); if(decimals > 0) { if(!dec_part) { return_text += "."; for(int i = 0; i < decimals; i++) return_text += "0"; } else { return_text += "."; for(int i = 0; i < decimals; i++) return_text += std::to_string((dec_part % ((int)pow(10, decimals - i))) / ((int)pow(10, decimals - i - 1))); } } return return_text; } void ActualConsoleOutTextError(std::string out_string) { #ifdef ANDROID_COMPILATION __android_log_print(ANDROID_LOG_ERROR, NAME, "%s", out_string.c_str()); #else std::cerr << out_string << std::endl; #endif } void ActualConsoleOutText(std::string out_string) { #ifdef ANDROID_COMPILATION __android_log_print(ANDROID_LOG_INFO, NAME, "%s", out_string.c_str()); #else std::cout << out_string << std::endl; #endif } std::string LayoutNameGenerator(int index) { if(index == STARTUP_FILE_INDEX) return std::string(NAME) + ".cfg"; return "layout" + std::to_string(index) + ".cfg"; } #ifdef ANDROID_COMPILATION ANativeActivity* getAndroidNativeActivity() { #ifdef SFML_SYSTEM_ANDROID return sf::getNativeActivity(); #else return NULL; #endif } #endif std::string LayoutPathGenerator(int index, bool created_proper_folder) { bool success = false; std::string cfg_dir; const char* env_p = NULL; #ifdef ANDROID_COMPILATION ANativeActivity* native_activity_ptr = getAndroidNativeActivity(); if(native_activity_ptr) env_p = native_activity_ptr->internalDataPath; #elif !(defined(_WIN32) || defined(_WIN64)) env_p = std::getenv("HOME"); #endif if(!created_proper_folder) env_p = NULL; if(env_p != NULL) cfg_dir = std::string(env_p) + "/"; cfg_dir += ".config/" + std::string(NAME); if(index == STARTUP_FILE_INDEX) return cfg_dir + "/"; return cfg_dir + "/presets/"; } std::string load_layout_name(int index, bool created_proper_folder, bool &success) { if(index == STARTUP_FILE_INDEX) { success = true; return "Initial"; } std::string name = LayoutNameGenerator(index); std::string path = LayoutPathGenerator(index, created_proper_folder); std::ifstream file(path + name); std::string line; success = false; if(!file.good()) { return std::to_string(index); } success = true; try { while(std::getline(file, line)) { std::istringstream kvp(line); std::string key; if(std::getline(kvp, key, '=')) { std::string value; if(std::getline(kvp, value)) { if(key == "name") { file.close(); return value; } } } } } catch(...) { success = false; } file.close(); return std::to_string(index); } //============================================================================ ConsumerMutex::ConsumerMutex() { count = 0; } void ConsumerMutex::update_time_multiplier(float time_multiplier) { if (time_multiplier <= 0) return; this->time_multiplier = time_multiplier; } double ConsumerMutex::get_time_s() { return 1.0 / ((base_time_fps) * (1.0 / this->time_multiplier)); } void ConsumerMutex::lock() { access_mutex.lock(); bool success = false; while (!success) { if (count) { count--; success = true; } else { condition.wait(access_mutex); } } access_mutex.unlock(); } bool ConsumerMutex::timed_lock() { std::chrono::durationmax_timed_wait = std::chrono::duration(this->get_time_s()); access_mutex.lock(); bool success = false; while (!success) { if (count) { count--; success = true; } else { auto result = condition.wait_for(access_mutex, max_timed_wait); if ((result == std::cv_status::timeout) && (!count)) break; } } access_mutex.unlock(); return success; } bool ConsumerMutex::try_lock() { access_mutex.lock(); bool success = false; if (count) { count--; success = true; } access_mutex.unlock(); return success; } void ConsumerMutex::unlock() { access_mutex.lock(); // Enforce 1 max count = 1; condition.notify_all(); access_mutex.unlock(); } //============================================================================ SharedConsumerMutex::SharedConsumerMutex(int num_elements) { this->num_elements = num_elements; if(this->num_elements <= 0) this->num_elements = 1; this->counts = new int[this->num_elements]; for(int i = 0; i < this->num_elements; i++) this->counts[i] = 0; } SharedConsumerMutex::~SharedConsumerMutex() { delete []this->counts; } void SharedConsumerMutex::update_time_multiplier(float time_multiplier) { if (time_multiplier <= 0) return; this->time_multiplier = time_multiplier; } double SharedConsumerMutex::get_time_s() { return 1.0 / ((base_time_fps) * (1.0 / this->time_multiplier)); } void SharedConsumerMutex::general_lock(int* index) { access_mutex.lock(); bool success = false; while (!success) { for (int i = 0; i < num_elements; i++) if (counts[i]) { counts[i]--; success = true; *index = i; break; } if (!success) condition.wait(access_mutex); } access_mutex.unlock(); } bool SharedConsumerMutex::general_timed_lock(int* index) { std::chrono::time_point clock_start = std::chrono::high_resolution_clock::now(); std::chrono::time_point clock_end = clock_start + std::chrono::microseconds((int)(this->get_time_s() * 1000 * 1000)); access_mutex.lock(); bool success = false; auto result = std::cv_status::no_timeout; while (!success) { for (int i = 0; i < num_elements; i++) if (counts[i]) { counts[i]--; success = true; *index = i; break; } if(!success) { const auto curr_time = std::chrono::high_resolution_clock::now(); std::chrono::durationtimed_wait = clock_end - curr_time; if (curr_time >= clock_end) result = std::cv_status::timeout; if (result == std::cv_status::timeout) break; result = condition.wait_for(access_mutex, timed_wait); } } access_mutex.unlock(); return success; } bool SharedConsumerMutex::general_try_lock(int* index) { access_mutex.lock(); bool success = false; for (int i = 0; i < num_elements; i++) if (counts[i]) { counts[i]--; success = true; *index = i; break; } access_mutex.unlock(); return success; } void SharedConsumerMutex::specific_unlock(int index) { if ((index < 0) || (index >= num_elements)) return; access_mutex.lock(); // Enforce 1 max counts[index] = 1; condition.notify_all(); access_mutex.unlock(); } void SharedConsumerMutex::specific_lock(int index) { if ((index < 0) || (index >= num_elements)) return; access_mutex.lock(); bool success = false; while (!success) { if(counts[index]) { counts[index]--; success = true; } else condition.wait(access_mutex); } access_mutex.unlock(); } bool SharedConsumerMutex::specific_timed_lock(int index) { if ((index < 0) || (index >= num_elements)) return false; std::chrono::time_point clock_start = std::chrono::high_resolution_clock::now(); std::chrono::time_point clock_end = clock_start + std::chrono::microseconds((int)(this->get_time_s() * 1000 * 1000)); access_mutex.lock(); bool success = false; auto result = std::cv_status::no_timeout; while (!success) { if (counts[index]) { counts[index]--; success = true; break; } if (!success) { const auto curr_time = std::chrono::high_resolution_clock::now(); std::chrono::durationtimed_wait = clock_end - curr_time; if (curr_time >= clock_end) result = std::cv_status::timeout; if (result == std::cv_status::timeout) break; result = condition.wait_for(access_mutex, timed_wait); } } access_mutex.unlock(); return success; } bool SharedConsumerMutex::specific_try_lock(int index) { if((index < 0) || (index >= num_elements)) return false; access_mutex.lock(); bool success = false; if (counts[index]) { counts[index]--; success = true; } access_mutex.unlock(); return success; }