bool EsINIParse(EsINIState *s) {
#define INI_READ(destination, counter, c1, c2) \
	s->destination = s->buffer, s->counter = 0; \
	while (s->bytes && *s->buffer != c1 && *s->buffer != c2) s->counter++, s->buffer++, s->bytes--; \
	if (s->bytes && *s->buffer == c1) s->buffer++, s->bytes--;

	while (s->bytes) {
		char c = *s->buffer;

		if (c == ' ' || c == '\n' || c == '\r') { 
			s->buffer++, s->bytes--; 
			continue;
		} else if (c == ';') {
			s->valueBytes = 0;
			INI_READ(key, keyBytes, '\n', 0);
		} else if (c == '[') {
			s->sectionClassBytes = s->keyBytes = s->valueBytes = 0;
			s->buffer++, s->bytes--;

			if (s->bytes && *s->buffer == '@') {
				s->buffer++, s->bytes--;
				INI_READ(sectionClass, sectionClassBytes, ' ', ']');
			}

			INI_READ(section, sectionBytes, ']', 0);
		} else {
			INI_READ(key, keyBytes, '=', '\n');
			INI_READ(value, valueBytes, '\n', 0);
		}

		return true;
	}

	return false;
}

bool EsINIPeek(EsINIState *s) {
	char *oldBuffer = s->buffer;
	size_t oldBytes = s->bytes;
	bool result = EsINIParse(s);
	s->buffer = oldBuffer;
	s->bytes = oldBytes;
	return result;
}

size_t EsINIFormat(EsINIState *s, char *buffer, size_t bytes) {
#define INI_WRITE(x, b) for (uintptr_t i = 0; i < b; i++) if (bytes) *buffer = x[i], buffer++, bytes--

	char *start = buffer;

	if (s->keyBytes || s->valueBytes) {
		if (s->key[0] == '[') return 0;
		INI_WRITE(s->key, s->keyBytes);
		if (s->key[0] != ';') INI_WRITE("=", 1);
		INI_WRITE(s->value, s->valueBytes);
		INI_WRITE("\n", 1);
	} else {
		INI_WRITE("\n[", 2);
		if (s->sectionClassBytes) INI_WRITE("@", 1);
		INI_WRITE(s->sectionClass, s->sectionClassBytes);
		if (s->sectionClassBytes && s->sectionBytes) INI_WRITE(" ", 1);
		INI_WRITE(s->section, s->sectionBytes);
		INI_WRITE("]\n", 2);
	}

	return buffer - start;
}

void EsINIZeroTerminate(EsINIState *s) {
	static char emptyString = 0;
	if (s->sectionClassBytes) s->sectionClass[s->sectionClassBytes] = 0; else s->sectionClass = &emptyString;
	if (s->sectionBytes) s->section[s->sectionBytes] = 0; else s->section = &emptyString;
	if (s->keyBytes) s->key[s->keyBytes] = 0; else s->key = &emptyString;
	if (s->valueBytes) s->value[s->valueBytes] = 0; else s->value = &emptyString;
}