mixer/src/settings.cpp

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
}
}