227 lines
6.8 KiB
C++
227 lines
6.8 KiB
C++
#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<char*, char*> entry : values) {
|
|
std::unordered_map<char*, char*, Djb12Hasher, StrcmpEqual>::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<char*, char*> 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
|
|
}
|
|
|
|
}
|