#include "settings.h" #include "msinclude.h" namespace ini { wchar_t* Utf8toUtf16(const char* str, uint64_t* size = nullptr) { if(!str || str[0] == '\0') return nullptr; int sizeNeeded = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); if(size) *size = sizeNeeded; wchar_t* utf16 = (wchar_t*)calloc(sizeNeeded, sizeof(wchar_t)); MultiByteToWideChar(CP_UTF8, 0, str, -1, utf16, sizeNeeded); return utf16; } UserSettings::UserSettings(char* textContents) { //Parsing values bool isCRLF = false; char *curLine = textContents; char *separator = nullptr, *key = nullptr, *value = nullptr; while(curLine) { char* nextLine = strchr(curLine, '\n'); if(nextLine == curLine + 1 || nextLine == curLine + 2) goto nextIteration; if (nextLine && (isCRLF || *(nextLine - 1) == '\r')) { isCRLF = true; *(nextLine - 1) = '\0'; } else if (nextLine) *nextLine = '\0'; // temporarily terminate the current line log_debugcpp("[SET] curLine: " + std::string(curLine) + " "); separator = strchr(curLine, '='); if(!separator) goto nextIteration; *separator = '\0'; key = trimAndAllocate(curLine); value = trimAndAllocate(separator + 1); values.try_emplace(key, value); log_debugcpp("[SET] ini Map size: " + std::to_string(values.size())); *separator = '='; nextIteration: if (nextLine) { // then restore newline-char, just to be tidy if (isCRLF) *(nextLine - 1) = '\r'; else *nextLine = '\n'; } curLine = nextLine ? (nextLine + 1) : NULL; } free(textContents); } char* const UserSettings::getValue(char* key, uint64_t len) { if (auto search = values.find(key); search != values.end()) return (char* const) search->second; return nullptr; } void UserSettings::setValue(char* key, char* value, uint64_t valueSize, uint64_t keySize) { char *newValue, *newKey; if (auto search = values.find(key); search != values.end()) { if(!(strcmp(value, search->second))) return; newValue = (char*)calloc(valueSize, sizeof(char)); if (!(search->second == pos || search->second == neg)) { free(search->second); } search->second = newValue; return; } newValue = (char*)calloc(valueSize, sizeof(char)); newKey = (char*)calloc(keySize, sizeof(char)); values.insert(std::make_pair(newKey, newValue)); return; } void UserSettings::setValue(char* key, bool value, uint64_t keySize) { char *newKey; log_debugcpp("[SET] Pos value: " + std::to_string((intptr_t)pos)); log_debugcpp("[SET] Neg value: " + std::to_string((intptr_t)neg)); if (auto search = values.find(key); search != values.end()) { log_debugcpp("[SET] Previous value: " + std::to_string((intptr_t)values[key])); if (!(search->second == pos || search->second == neg)) { free(search->second); } if (value) search->second = pos; else search->second = neg; return; } newKey = (char*)calloc(keySize, sizeof(char)); memcpy(newKey, key, keySize * sizeof(char)); values.insert(std::make_pair(newKey, value ? pos : neg)); return; } bool UserSettings::save(const char* path) { if(!path) return false; uint64_t convertedPathSize = 0; wchar_t* utf16Path = Utf8toUtf16(path, &convertedPathSize); if(!utf16Path) return false; #define releaseBeforeReturn() do { \ CloseHandle(settingsHandle); \ settingsHandle = nullptr; \ free(utf16Path); \ free(text); \ } while(0) //We initially reserve 1024B for flushing. If storage is exceeded, more same-size chunks are allocated const uint64_t chunkSize = 1024; char* text = (char*)calloc(chunkSize, sizeof(char)); uint64_t mapSize = values.size(); uint64_t chunks = 1; uint64_t keySize = 0, valueSize = 0, totalSize = 0, previousStepSize = 0; //for(std::pair entry : values) { std::unordered_map::iterator it; for (it = values.begin(); it != values.end(); it++) { keySize = strlen(it->first); valueSize = strlen(it->second); totalSize += valueSize + keySize + (it == values.begin() ? 1 : 2); //newline and separator if(totalSize > (chunkSize * chunks)) { text = (char*)realloc(text, (++chunks * chunkSize)); } if(it != values.begin()) memcpy(text + previousStepSize++, "\n", sizeof(char)); memcpy(text + previousStepSize, it->first, sizeof(char) * keySize); memcpy(text + previousStepSize + keySize, "=", sizeof(char)); memcpy(text + previousStepSize + 1 + keySize, it->second, sizeof(char) * valueSize); previousStepSize = totalSize; } HANDLE settingsHandle = nullptr; settingsHandle = CreateFile2( utf16Path, GENERIC_READ | GENERIC_WRITE, 0, CREATE_ALWAYS, NULL); if(settingsHandle == INVALID_HANDLE_VALUE) { log_debugcpp("[SET] Can't save to file: " + std::to_string(GetLastError())); releaseBeforeReturn(); return false; } DWORD bytesWritten; BOOL writeSuccess = WriteFile( settingsHandle, text, totalSize, &bytesWritten, nullptr ); releaseBeforeReturn(); if (writeSuccess == TRUE) return true; else return false; return false; #undef releaseBeforeReturn } UserSettings::~UserSettings() { //if(textContents) free(textContents); for(std::pair entry : values) { free(entry.first); if (!(entry.second == pos || entry.second == neg)) free(entry.second); } } UserSettings* UserSettings::createSettings(const char* path, bool create) { if(!path) return nullptr; wchar_t* utf16Path = Utf8toUtf16(path); if(!utf16Path) return nullptr; #define releaseBeforeReturn() do { \ CloseHandle(settingsHandle); \ settingsHandle = nullptr; \ free(utf16Path); \ } while(0) char* textContents; HANDLE settingsHandle = nullptr; settingsHandle = CreateFile2( utf16Path, GENERIC_READ | GENERIC_WRITE, 0, (create ? OPEN_ALWAYS : OPEN_EXISTING), NULL); if(settingsHandle == INVALID_HANDLE_VALUE) { log_debugcpp("[SET] Can't create settings file: " + std::to_string(GetLastError())); releaseBeforeReturn(); return nullptr; } //Calculating file size and reading file uint64_t fileSize; LARGE_INTEGER fileSizeStruct; if(!GetFileSizeEx(settingsHandle, &fileSizeStruct)) { releaseBeforeReturn(); return nullptr; } fileSize = fileSizeStruct.QuadPart; uint32_t bytesRead = 0; uint64_t textContentsSize = fileSize + 1; textContents = (char*)calloc(textContentsSize, sizeof(char)); if (ReadFile(settingsHandle, textContents, fileSize, (LPDWORD)&bytesRead, NULL) != TRUE) { releaseBeforeReturn(); return nullptr; } releaseBeforeReturn(); return new UserSettings(textContents); //textContents.assign(tempTextContents); //free(tempTextContents); #undef releaseBeforeReturn } }